目录
一、异步编程概念
异步编程是可以让程序并行运行的一种手段,其可以让程序中的一个工作单元与主应用程序线程分开独立运行,并且在工作单元运行结束后,会通知主应用程序线程它的运行结果或者失败原因。使用异步编程可以提高应用程序的性能和响应能力。
同步点餐:一个服务员来记菜
异步点餐:服务员给菜单客户点餐
异步点餐不会提升单个客户点餐的速度
web服务器能够同时服务的请求数量有限
async、await不等于“多线程”
二、异步原理
对于不支持的异步方法怎么办?wait()(无返回值);Result(有返回值)。风险:死锁。尽管不用
await、async是语法糖,最终编译成“状态机调用”
async的方法会被C#编译器编译成一个类,会主要根据await调用进行切分为多个状态,对async方法的调用会被拆分为对MoveNext的调用
用await看似是“等待”,经过编译后,其实没有“wait”
三、异步方法的定义
在.net中所谓的异步方法,一般是指async关键字修饰的方法。该方法有如下特点:
异步方法的返回值一般是Task<T>,T是真正的返回值类型,如Task<int>。即使方法没有返回值,也最好把返回值声明为非泛型的Task。(按钮等控件事件响应方法用void)
异步方法名字以Async结尾。
调用异步方法时,一般方法前面加上await关键字,这样返回值就是泛型指定的T类型。
一个方法中如果有await调用的异步方法,那么该方法也必须是async修饰的异步方法。
四、异步方法的线程委托
在一些情况下我们可能会将异步方法放到线程池来执行。
如果该方法是用正则表达式写的匿名方法的话,则只需要在前面使用async关键字即可。异步方法不等于多线程 ,异步方法的代码并不会自动在新线程中执行,除非把代码放到新线程中执行
把要执行的代码以委托的形式传递给Task.Run(),这样就会从线程池中取出一个线程执行我们的委托await Task.Run(()=>{//耗时操作代码,可以return返回值})
五、异步方法不要使用Sleep
如果想要在异步方法中暂停一段时间,不要用Sleep方法,因为它会阻塞调用线程,降低并发。如果想在异步方法中暂停一段时间,不要用Thread.Sleep(),因为它会阻塞调用线程,而要用await Task.Delay()。在控制台中没看到区别,但是放到winform程序中就能看到区别了。asp.net core中也看不到区别但是sleep()会降低并发,用点餐举例子
Thread.Sleep(3000); // 暂停3000ms,会阻塞线程调用
如果要实现类似效果的话,可以用await关键字加Delay方法 ,用法如下
await Task.Delay(); //异步暂停3000ms
六、异步的WhenAll
Task类的重要方法:
1、Task<Task> WhenAny(IEnumerable<Task> tasks)等,任何一个Task完成,Task就完成
2、Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks)等,所有Task完成,Task才完成。用于等待多个任务执行结束,但是不在乎它们的执行顺序
3、FromResult()创建普通数值的Task对象
七、为什么有的异步方法没标注async
async方法缺点:
1、异步方法会生成一个类,运行效率没有普通方法高
2、可能会占用非常多的线程
没有用async的好处:只甩手Task,不“拆完了再装”反编译上面的代码只是普通的方法调用,优点:运行效率更高,不会造成线程浪费
返回值为Task的不一定都标注async,标注async只是让我们可以更方便的await而已
如果一个异步方法只是对别的异步方法调用的转发,并没有太多复杂的逻辑(比如等待A的结果,在调用B;把A调用的返回值拿到内部做一些处理再返回),那么就可以去掉async关键字