Thread类是C#语言对线程对象的一个封装
学习多线程之前我们先了解一下电脑的一些概念,比如进程,线程,这个参考https://www.cnblogs.com/loverwangshan/p/10409755.html 这篇文章。今天我们接着来介绍同步方法和异步方法。
一:同步方法:在程序继续执行之前需要等待同步方法执行完毕返回结果
通俗的例子就是: 邀请wss,wss要忙一会儿,邀请人等着wss完成后,再一起去吃饭,这就是所谓的诚心诚意的请人吃饭。下面我通过代码来举例来说明一下同步方法:
1 /// <summary> 2 /// 一个比较耗时耗资源的私有方法 3 /// </summary> 4 /// <param name="name"></param> 5 private void DoSomethingLong(string name) 6 { 7 Console.WriteLine($"******DoSomethingLong Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}*******"); 8 long lResult = 0; 9 for (int i = 0; i < 1_000_000_000; i++) 10 { 11 lResult += i; 12 } 13 Console.WriteLine($"*****DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}*********"); 14 } 15 16 /// <summary> 17 /// 同步方法 18 /// </summary> 19 private void syncWay() 20 { 21 Console.WriteLine($"******同步方法开始执行,执行线程ID:【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】,执行开始时间:【 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}】*****"); 22 for (int i = 0; i < 5; i++) 23 { 24 string name = string.Format($"sysnc_{i}"); 25 this.DoSomethingLong(name); 26 } 27 Console.WriteLine($"****同步方法执行结束,执行线程ID:【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】,执行开始时间:【 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}】******"); 28 }
直接调用执行 syncWay(); 则会输出如下图:
我们可以通过上图晓得:DoSomethingLong是一步一步执行的,而且同步方法的线程Id都是一致的,也可以看出执行过程的耗时也蛮长。
二:异步方法:在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作
通俗的例子就是: 邀请wss,wss要忙一会儿,然后wss自己先忙着吧,邀请人先去吃饭了,wss忙完自己去吃饭吧,这就是所谓的客气一下的请人吃饭。下面我接着使用实例来认识异步方法:
1 /// <summary> 2 /// 一个比较耗时耗资源的私有方法 3 /// </summary> 4 /// <param name="name"></param> 5 private void DoSomethingLong(string name) 6 { 7 Console.WriteLine($"*****DoSomethingLong开始;参数【{name}】;线程Id:【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】;当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***"); 8 long result = 0; 9 for (int i = 0; i < 1_000_000_000; i++) 10 { 11 result += i; 12 } 13 Console.WriteLine($"*****DoSomethingLong结束;参数【{name}】;线程Id:【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】;当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")};result:{result}***"); 14 } 15 16 private void AsyncWay() 17 { 18 Console.WriteLine($"******异步方法开始执行,执行线程ID:【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】,执行开始时间:【 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}】*****"); 19 Action<string> action = this.DoSomethingLong; 20 for (int i = 0; i < 5; i++) 21 { 22 string name = string.Format($"async_{i}"); 23 action.BeginInvoke(name, null, null);//委托自身需要的参数 + 2个异步参数(这两个参数我们在下面详细说明) 24 } 25 Console.WriteLine($"****异步方法执行结束,执行线程ID:【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】,执行开始时间:【 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}】******"); 26 }
然后通过执行AsyncWay()这个方法,会得到如下截图:
我们可以通过上面的图得到如下几点:
1:不卡主线程改善用户体验。web或者winform如果处理耗时的任务,异步方法会使页面或者winform不卡界面,这主要是主线程闲置,计算任务交给子线程完成。这个可以改善用户体验。一般在web应用中异步方法会做发送短信/记录日志等功能。
2:异步方法耗时短,这个是最明显的。通过我们观察cpu的曲线图,会总结出:多线程其实是资源换性能。但是也不能乱用多线程,主要是
- A: 资源不是无限的
- B: 资源调度损耗(这个电脑配置有关系)
3:异步方法是无序的,主要体现在如下:
- A:启动无序,因为线程资源是向操作系统申请的,由操作系统的调度策略决定,所以启动顺序随机
- B:同一个任务同一个线程,执行时间也不确定,CPU分片
- C:由上面两条也得到结束也无序
三:异步方法如何控制顺序?就是说我们想要在一个异步方法全部执行完成后再执行某一部分内容
可以通过以下几种方式来实现:
1:使用BeginInvoke,这个我们上面有提到,下面还是先根据代码来分析这个方法的应用。
实在太晚,明天接着补充!
异步编程概览
.NET Framework 允许您异步调用任何方法。定义与您需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。
BeginInvoke 方法用于启动异步调用。它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)。
BeginInvoke 立即返回,不等待异步调用完成。
BeginInvoke 返回 IasyncResult,可用于监视调用进度。
EndInvoke 方法用于检索异步调用结果。调用 BeginInvoke 后可随时调用 EndInvoke 方法;如果异步调用未完成,EndInvoke 将一直阻塞到异步调用完成。
EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由BeginInvoke 返回的 IAsyncResult。
四种使用 BeginInvoke 和 EndInvoke 进行异步调用的常用方法。调用了 BeginInvoke 后,可以:
1.进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。
2.使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用EndInvoke。这里主要是主程序等待异步方法,等待异步方法的结果。
3.轮询由 BeginInvoke 返回的 IAsyncResult,IAsyncResult.IsCompeted确定异步调用何时完成,然后调用 EndInvoke。此处理个人认为与相同。
4.将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。这是在强制装换回调函数里面IAsyncResult.AsyncState(BeginInvoke方法的最后一个参数)成委托,然后用委托执行EndInvoke。
警告 始终在异步调用完成后调用 EndInvoke。