参考:
官方文档 c#使用 Async 和 Await 的异步编程 、 c#异步编程、 .NET 中的并行处理、并发和异步编程 前台和后台线程
异步与多线程的区别(异步是目的,多线程是实现它的一种方式,异步的优先级有时候比主线程还高) - 子福当自强 - 博客园
.NET 异步详解 --能说清楚异步与多线程的区别,涉及到操作系统原理(重点)
await,async 我要把它翻个底朝天,这回你总该明白了吧
Nodes:Node.js异步机制和基本原理
Nodes异步是基于事件机制,.net异步是基于任务的异步编程模型
异步开发是什么:
异步开发,就是利用线程技术,当执行一个占用时间长的任务时,不会阻止用户其它工作,而且其完成时会通知用户。
虽然是通过多线程来实现异步,但是因为一般异步时耗时的操作都不是针对CPU的,所以对CPU压力影响不大。
常用的是 基于任务的异步模式 (TAP)
异步本质是通过线程池提高线程的利用率。
- 异步采用IO的DMA模式,不会消耗CPU资源。计算密集的工作,采用多线程。IO密集的工作,采用异步
- 举例:网络爬虫爬数据,如果数据很庞大,这个时候就需要使用异步了。
- DMA:Direct Memory Access是IO的操作模式,可以直接访问内存,不经过CPU,不消耗CPU资源。
- 异步和多线程区别就是,充分利用DMA释放CPU压力。
- 异步实现方式:.net中实现异步的方式有定时器和多线程,一般都是使用多线程。
使用场景
- 要执行耗时且不需要立刻返回结果的操作,如:I/O操作、网络操作
争议:
有种说法是:异步是不阻塞当前主线程执行,通过子线程去执行,还有总说法刚好相反,具体参考:异步与线程阻塞
疑问:多线程是实现异步的一种方式,还可以通过定时器来实现异步,又有人说异步不是多线程,异步是把前台现成改为后台现成而已? 异步和多线程Thread
同步、异步、并行区别
同步:代码执行顺序默认是一行一行执行的,要等当前行的代码执行完后,才会执行下一行的代码。
异步:不用等当前行代码执行完毕就会执行下一行的代码。异步编程是一项关键技术,可以直接处理多个核心上的阻塞 I/O 和并发操作
并行:许多个人计算机和工作站都有多个 CPU 内核,以便多个线程能够同时执行。 为了利用硬件,你可以对代码进行并行化,以将工作分摊在多个处理器上。
并行和多线程、异步和多线程有啥关系?
异步和多线程:多线程是创建多个线程,消耗的是CPU,异步呢?
异步方法返回类型:void、Task、Task<TResult>
异步编程模式:
- 基于任务的异步模式 (TAP):使用Task、async 、await 方式,推荐
-
Task类:示一个异步操作。要求.NET Framework4.0及以上版本
-
Task.WhenAll:所有提供的任务已完成时,创建将完成的任务。要求.NET Framework4.5及以上版本
- 可以通过WhenAll(方法1,方法2,方法3)方式,也可以通过Task[]数组方式
- Task.WaitAny (Task []):等待任何提供的Task对象在指定的时间间隔内完成执行
- WaitAny(Task [],Int32): 等待任何提供的Task对象在指定的毫秒数内完成执行
- Task.ContinueWith :创建一个在目标 Task 完成时异步执行的延续任务。要求.NET Framework4.0及以上版本
- 直接用lambda表达式创建方法ContinueWith(((t) => { 方法体; })),或则调用方法ContinueWith(Action<Task>)...
-
-
async:使用 async 修饰符可将方法指定为异步方法
-
await:等待,await 运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成。
-
-
基于事件的异步模式 (EAP):不推荐
- 异步编程模型 (APM) :不推荐
- 使用委托进行异步编程
- BeginInvoke:在与 Dispatcher 关联的线程上异步执行委托
- EndInvoke:异步执行完毕后获取异步方法返回值
- AsyncCallback:引用在相应异步操作完成时调用的方法。
- 使用异步方式调用同步方法
- 使用委托进行异步编程
示例
使用Task、async 、await 方式实现3个打印
用WindowsForm创建一个叫“异步”的按钮,按钮内有三行代码分别打印的3个label的时间,打印2时使用的是异步方法,暂停3秒
//异步按钮点击事件 private void button1_Click(object sender, EventArgs e) { //打印1 label1.Text = "打印1:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId; //打印2,异步方法 Test(); //打印3 label3.Text = "打印3:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId; } //异步方法:启动另外一个线程 public async Task Test() { await Task.Run(() => { Thread.Sleep(3000); //暂停3秒 label2.Text = "打印2:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId; }); }
效果:打印1显示,打印3跟着显示,打印2最后才出来,且打印2的线程ID都是不一样的,如下
使用几个打印2异步方法基本同时执行,等待全部异步方法执行完毕后提示信息
//异步按钮点击事件 private void button1_Click(object sender, EventArgs e) { //打印1 Console.WriteLine("打印1:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); //打印2,调用异步方法 var listTask = new List<Task>(); listTask.Add(Test());//往List<Task>集合中添加任务 listTask.Add(Test()); listTask.Add(Test()); Task.WhenAll(listTask.ToArray()).ContinueWith(((b) => { Console.WriteLine("异步完毕:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); })); //打印3 Console.WriteLine("打印3:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); } // 异步方法:启动另外一个线程 public async Task Test() { await Task.Run(() => { Thread.Sleep(3000); //暂停3秒 Console.WriteLine("打印2:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); }); }
执行效果:几个异步的打印2方法基本同时执行,等打印2完毕后,在提示异步完成
使用异步委托模式来打印2
//异步按钮点击事件 private void button1_Click(object sender, EventArgs e) { //打印1 Console.WriteLine("打印1:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); //打印2:使用异步委托方式 Action<int> d1 = oldTest; d1.BeginInvoke(2000, ((t)=> { Console.WriteLine("单个异步执行完成"); }), null); //打印3 Console.WriteLine("打印3:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); } public delegate void TaskWhileDelegate(int ms); //定义委托,异步时使用 public void oldTest(int ms) { Thread.Sleep(ms); //暂停3秒 Console.WriteLine("异步委托打印2:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + ",线程ID:" + Thread.CurrentThread.ManagedThreadId); }
执行效果
耗时方法超时后直接返回,不再执行
如果test()方法执行要5秒,现在设置为3秒,那3秒后直接停止执行跳过这个方法
如果test()方法执行要2秒,现在设置为3秒,那2秒后执行完跳过
Task[] task = new Task[] { Task.Run(() => test()) }; Task.WaitAny(task, 3000);
问题:
- 以前的一个老项目是用WebForm做的项目,是.net framework 3.5的,想使用异步,就把项目升级到.net framework 7.2,结果发现用异步委托方式和最新的Task方式都不行,如果直接创建一个新的WebForm项目又可以,所以暂时不知道是哪里的问题,暂时记下,以后再深入探究