什么是异步编程
每次启动程序时,系统会自动在内存中创建一个进程。进程是构成运行程序的资源的集合。这些资源包括虚地址空间、文件句柄和许多其他程序运行所需的资源。
在进程的内部,系统会创建一个称为线程的内核(Kernel)的对象,它代表了真正的运行程序。线程是执行线程的简称。当进程建立,系统就会由主程序的Main方法的第一行语句处开始了线程的执行。
- 在默认情况下,一个进程只包含一个线程,即从程序的开始,一直执行到结束。
- 线程是可以派生其他线程,在任意时刻,一个进程都可以包含不同状态的多个线程,来执行程序的不同部分。
- 如果一个进程拥有多个线程,它们将共享进程的资源。
- 系统为处理器规划的执行单元,是线程而非进程。
在很多时候,我们在进程中使用单一线程从头到尾地执行程序,这种简单模式会导致性能和用户体验令人难以接受。
比如程序向另外一台服务器发出请求,由于网络等外部原因,此种通信任务往往会耗费大量时间,进程如果在此期间仅仅只能等待网络或网络上其他机器的响应,将严重地降低了性能。程序不应该浪费等待的时间,而应该更加高效地利用,在等待的时间执行其他任务,回复到达后再继续执行第一个任务。
如果程序调用某个方法,等待其执行全部处理后才能继续执行,我们称其为同步的。相反,在处理完成之前就返回调用方法则是异步的。
我们在编程语言的流程中添加了异步控制的部分,这部分的编程可以称之为异步编程。
同步~同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去
异步~异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
异步是一种目的。手段有 异步委托、多线程、线程池等...
.NET异步编程的发展历程
异步编程四个发展过程,参考:[你必须知道的异步编程]
NET1.1 APM(异步编程模型):.net 1.0时期就提出的一种异步模式,基于IAsyncResult接口实现BeginXXX和EndXXX类似的方法。HttpWebRequest就实现了该模式(继承IAsyncResult接口并且实现BeginXXX和EndXXX方法)
NET2.0 EAP(基于事件的异步编程模型):实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步方法的取消、进度报告和报告结果。然而.net中并不是所有的类都支持EAP。当调用基于事件的EAP模式的类的XXXAsync方法时,就开始了一个异步操作,并且基于事件的EAP模式是基于APM模式之上的,而APM又是建立在委托之上的。
NET4.0 TAP(基于任务的异步编程模型):.net 4.0为我们带来了Task的异步,我们有以下4种方法创建Task:
1,Task.Factory.StartNew(),比较常用。
2,Task t1 = new Task(() => { Console.WriteLine("t1 start"); }); t1.Start();
3,Task.Run(),是.net 4.5中增加的。
4,Task.FromResult(),如果结果是已计算,就可以使用这种方法来创建任务。
NET4.5 异步和等待(async和await):
使用async 和await关键字来定义异步方法,使得代码就像定义同步方法一样简单。
注意:使用async 和await定义异步方法不会创建新线程,它运行在现有线程上执行多个任务.
综上:以上几种模型本质都是使用线程池和委托机制的。
线程池
托管线程池
1、在 Framework 4 及更高版本中使用线程池容易得多,因为可以创建在线程池线程上执行异步任务的 Task 和 Task<TResult> 对象。
2、特征:线程池线程是后台线程。
通过这种重复使用,应用程序可以避免产生为每个任务创建新线程的开销。
每个进程只有一个线程池。
3、线程池最大线程数
进程可以调用 ThreadPool.GetMaxThreads 方法,以确定线程数。
可以使用 ThreadPool.GetMaxThreads 和 ThreadPool.SetMaxThreads 方法来控制最大线程数。
任务Task
Task是.NET4.0加入的,跟线程池ThreadPool的功能类似,用Task开启新任务时,会从线程池中调用线程,而Thread每次实例化都会创建一个新的线程。
注意:用Task方式启动的代码 不能控制其优先级,因为是采用的线程池。用Thread才可以设置优先级。
可查看官网:何时不使用线程池线程
创建Task的一般方法有:
1、直接实例化:必须手动去Start
2、工厂模式创建,直接执行
3、Run方法创建,直接执行
Task的生命周期:
方法名 说明
Created 表示默认初始化任务,但是“工厂创建的”实例直接跳过。
WaitingToRun 这种状态表示等待任务调度器分配线程给任务执行。
RanToCompletion 任务执行完毕。
Task的任务控制:
方法名 说明
Task.Wait task1.Wait(); 就是等待任务执行(task1)完成,task1的状态变为Completed。
Task.WaitAll 待所有的任务都执行完成:
Task.WaitAny 等待任何一个任务完成就继续向下执行
Task.ContinueWith 第一个Task完成后自动启动下一个Task,实现Task的延续
CancellationTokenSource 通过cancellation的tokens来取消一个Task。
1、Task Wait方法不会等待子Task完成
所谓的子Task,就是在一个Task中再创建一个Task,也就是嵌套Task。
在主线程中等待后台线程执行完毕,可以使用Wait方法(会以同步的方式来执行)。不用Wait则会以异步的方式来执行。
测试:
private void ParentTask() { Task.Factory.StartNew(() => { SonTask(); }); Thread.Sleep(100); Console.WriteLine("主Task执行完毕!"); } private void SonTask() { Thread.Sleep(1000); Console.WriteLine("子Task执行完毕!"); } [STAThread] static void Main() { Task parentTask = Task.Factory.StartNew(() => { ParentTask(); }); parentTask.Wait(); //等待parentTask完成 Console.WriteLine("主进程执行"); Console.ReadLine(); }