page contents

C#多线程的实现方式及并发处理方式

本文讲述了C#多线程的实现方式及并发处理方式!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

attachments-2022-10-6rqN3hjX634cb905cc482.png

本文讲述了C#多线程的实现方式及并发处理方式!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

随着.net版本不断升级,目前多种多线程实现方法

一 .Thread    最基本

1.优缺点

优点--Thread API丰富

缺点--   1.线程资源是操作系统管理的,对API响应并不灵敏,(也就是调用一次提供的API可能不会立即响应)难以控制
    2.线程启动数量是没有控制的,可能会导致死机等意外发生

2.Thread对象实例化方法(四种)

  2.1声明一个无参的、返回值为void的委托ThreadStart,委托内含一个静态方法;

  2.2 声明一个无参的、返回值为void的委托ThreadStart,委托内含一个对象方法;

  2.3 直接使用匿名委托;

  2.4 直接使用Lambda表达式;

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"主线程,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Thread thread1 = new Thread(new ThreadStart(NewThreadDisplay));//第一种
 
            AnotherObject another = new AnotherObject();
            Thread thread2 = new Thread(new ThreadStart(another.AnotherThreadDisplay));//第二种
 
            Thread thread3 = new Thread(delegate() { Console.WriteLine($"子线程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } );//第三种
            Thread thread4 = new Thread(()=> Console.WriteLine($"子线程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}"));//第四种
 
            thread1.Start();//启动子线程1
            thread2.Start();//启动子线程2
            thread3.Start();//启动子线程3
            thread4.Start();//启动子线程4
            Console.ReadKey();
        }
 
    static void NewThreadDisplay()
        {
            Console.WriteLine($"子线程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
        }
    }
    class AnotherObject
    {
        public void AnotherThreadDisplay()
        {
            Console.WriteLine($"子线程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
        }
    }
}

输出结果:

主线程,ThreadId: 1
子线程3,ThreadId: 5
子线程4,ThreadId: 6
子线程1,ThreadId: 3
子线程2,ThreadId: 4

 可以看出多线程启用时无序的,即使一个线程在是先start的,执行也可能在后start的线程后面;并且每次执行的先后顺序都不一定相同

3.关于线程优先级 Priority属性
Priority属性是一个ThreadPriority型枚举,列举了5个优先等级:Highest、AboveNormal、Normal(普通线程默认)、BelowNormal、Lowest

4.关于线程的状态
通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。
通过CurrentContext可以获取线程当前的上下文。
CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。

5.列举一些API(可能有坑)
终止-Abort() GetDomain()-返回当前线程正在其中运行的当前域 GetDomainId()   Interrupt()-中断处于   WaitSleepJoin 线程状态的线程

Join() - 已重载。 阻塞调用线程,直到某个线程终止时为止    Resume()-继续运行已挂起的线程    Start()-执行本线程   Suspend()-挂起当前线程   

Sleep()- 把正在运行的线程挂起一段时间

6.关于前台线程和后台线程

前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程都是前台线程
后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。
通过BeginXXX方法运行的线程都是后台线程。
注1:后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。

 

二. ThreadPool

.NetFramework 2.0 新增
基于池化资源管理设计思想,线程是一种资源,每次要用线程,就去申请一个线程,使用完释放;池化就是一种容器,容器提前申请5个线程,程序需要使用线程直接找容器获取,

用完再放回(空置状态,避免频繁申请和销毁,容易还会根据限制的数量去申请和释放)
优点:1线程复用 2限制最大线程数量
缺点:API太少了,在线程顺序控制上弱,用起来不方便

 

三.Task--.NetFramework 3.0 新增

比较常用的多线程方式,全部是线程池线程、提供丰富API

Task线程全部是线程池线程、提供丰富API


Action action = o=>{xxxx;}
Task task =new Task(action);
task.Start();

举例:

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        { 
            Console.WriteLine($"主线程开始,ThreadId: {Thread.CurrentThread.ManagedThreadId}");

            Task task1 = new Task(() => Console.WriteLine($"子线程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}"));
            task1.Start();

            //也可以直接创建并启用
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => Console.WriteLine($"子线程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));
            taskList.Add(Task.Run(() => Console.WriteLine($"子线程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));
            taskList.Add(Task.Run(() => Console.WriteLine($"子线程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));

            Task.WaitAny(taskList.ToArray());////阻塞当前线程,直到任一任务结束
            Console.WriteLine($"有一个子线程执行完毕,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Task.WaitAll(taskList.ToArray());////阻塞当前线程,直到全部任务结束
            Console.WriteLine($"所有子线程执行完毕,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.WriteLine($"主线程结束,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.ReadKey();
        }

    }
}

执行结果:

主线程开始,ThreadId: 1
子线程1,ThreadId: 4
子线程2,ThreadId: 7
子线程3,ThreadId: 8
子线程4,ThreadId: 4
有一个子线程执行完毕,ThreadId: 1//阻塞了主线程执行!
所有子线程执行完毕,ThreadId: 1////阻塞了主线程执行!
主线程结束,ThreadId: 1

通过结果发现,无论怎么执行,最后三行输出的顺序都是一样的!可见通过waitAny()、waitAll()方法可以阻塞主线程的执行,已经等达到一些顺序控制的目的了!



注:1.尽量不要线程套线程,有更优秀的方法
2.子线程不能直接操作界面
等全部任务完成后启动一个新的task完成后续动作
TaskFactory taskFactory =new TaskFactory();
taskFactory.ContinueWhenAll(taskList.ToArray(),tArray=>{
xxxx
})
等任一任务完成后启动一个新的task完成后续动作
taskFactory.ContinueWhenAny(taskList.ToArray(),tArray=>{
xxxx
})
//continue的后续线程,不可能是主线程,其余皆可能

 

四.Parallel并行编程
主线程也参与计算,介于线程,节约一个线程
通过指定ParallelOptions控制最大并发数量
Parallel.Invoke(()=>{111;},()=>{222;},()=>{333;})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"主线程开始,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Parallel.Invoke(new ParallelOptions() { MaxDegreeOfParallelism = 4},
                              () => {
                                  Console.WriteLine($"子线程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                Thread.Sleep(1000); },
                              () => {
                                  Console.WriteLine($"子线程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                Thread.Sleep(1000);},
                              () => {
                                  Console.WriteLine($"子线程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              },
                              () => {
                                  Console.WriteLine($"子线程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              },
                              () => {
                                  Console.WriteLine($"子线程5,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              });
            Console.WriteLine($"主线程结束,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.ReadKey();
        }
 
    }
}

  

输出结果:

主线程开始,ThreadId: 1
子线程1,ThreadId: 1
子线程2,ThreadId: 3
子线程3,ThreadId: 5
子线程4,ThreadId: 4
子线程5,ThreadId: 1
主线程结束,ThreadId: 1

特点:主线程(1)也参与计算,节约一个线程;可以通过ParallelOptions控制最大并发数量(本例设置为4),子线程5与线程1使用了同一个线程,所以5等1执行完了之后才会打印,同理主线程结束也在最后完成;

 

五、BeginInvoke()线程封装相关的

委托.BeginInvoke()自动启用异步多线程,并且带 回调 BeginInvoke()

3.利用委托方式在子线程中改变主线程UI的内容

    private delegate void delegateUpdateLabel(string text);
    private void UpdateLabel(string text)
        {
            if (this.updateProcess_label.InvokeRequired)
            {
                Invoke(new delegateUpdateLabel(UpdateLabel), new object[] { text });
            }
            else
            {
                updateProcess_label.Text = text;
            }
        }
    IAsyncResult asyncResult1 = action.BeginInvoke("", null, null);    
    IAsyncResult asyncResult2 = func.BeginInvoke(null, null);

判断异步线程完成方式:

1.利用asyncResult.IsCompleted 2.利用信号量asyncResult.AsyncWaitHandle.WaitOne(),可以做超时判断 ---waitOne(-1)一直等待 waitOne(100)等待100ms
带返回值的异步调用方法 returnValue = func.EndInvoke(asyncResult2) EndInvoke()也能用在回调里,但只能用一次

TODO...

更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

长按或扫描下方二维码,免费获取 Python公开课和大佬打包整理的几百G的学习资料,内容包含但不限于Python电子书、教程、项目接单、源码等等

attachments-2022-10-kwwbZ9WG6347756cbf77c.jpg

  • 发表于 2022-10-17 10:08
  • 阅读 ( 2600 )
  • 分类:C/C++开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章