当前位置: 首页 > 科技观察

C#异步编程中Async的正名

时间:2023-03-17 21:00:53 科技观察

半年前翻译了一系列很烂的异步编程文章,使用异步通用语:《将来的某个时候》我会重新翻译C#5.0中的Async。写在前面  异步编程在处理并发的时候用的越来越多。之所以说上面这句话,是为了区分多线程编程。各位司机都知道,其实异步编程的核心目标就是并发处理。但是常常会有一些无奈的说法和疑问,比如异步编程能否提高应用程序性能?他能缩短我花在任务上的时间吗?他会阻塞线程吗?如果线程没有阻塞,为什么断点不继续往下执行,兄弟!线程释放到哪里去了?我读书不多,别骗我,线程释放了,程序怎么跑?我前台用了Ajax,后台有必要用Async吗?可能作为司机的你看到最后一个问题,不得不摊开双手┑( ̄Д ̄)┍。多线程场景理解也许在某个时候,你想提高应用程序的执行速度,尽快得到结果。这时候绝对不要选择Async和Task。例如,你和你的妻子周末去超市购物。一进超市,发现每条结账队伍都有几十个人,于是用了多线程。你去排队,一个一个往前走,你的妻子在另一端购物。当你正要走到收银台时,你的妻子过来把购物车推给你,于是你结账回家。虽然这种行为很不文明,但是这是多线程,和异步编程无关。异步编程场景了解什么是异步编程,它能解决什么问题?你和你的妻子开了一家面包店,最初只有你们两个服务顾客。没想到新店开张这么火爆,每分钟来一个顾客,烤一个面包要两分钟。每次有客人来,你拿一块面包到后厨的烤箱烤,你和老婆还要花两分钟时间等各自的烤箱完成任务。但是在你等待的两分钟内,又来了两个顾客。照这样的速度下去,根本无法满足客户的需求!您已经发现了您和您妻子的问题:您和您妻子的两个线程都被烤箱占用的时间阻塞了!为了解决堵车的问题,你和老婆又买了两个烤箱,为了避免新顾客没有服务,每次送面包进烤箱,标记是哪个顾客的,马上退回,随时准备接收新的customers如果有顾客来,会马上接待,新面包会被送到另一个烤箱做标记,马上返回等待其他人上菜。面包烤好后,烤箱会发出“叮”的一声。注意,这个信号到来后,你不需要去后面的厨房烤箱取面包,而是你和你老婆不忙的人去取。这样处理后,高并发客户数对你来说就得心应手了。作为两个线程,你和你老婆可以以非阻塞的形式(不等待烤箱)不断地返回给客户。但是需要注意的是非阻塞的概念,他不让你的程序继续往下执行。就烤面包而言,你的一种烤法是这样的:1.把面包送到烤箱2.烤箱处理面包,给你结果3.拿到面包,发给客户。所以“非阻塞”的概念不允许你直接进入第三步。在非阻塞期间,你的方法中没有线程。这个方法还是要按照时间来等待,等待以后某个时间有信号叫醒你或者你老婆,然后这个方法恢复执行。因此,程序的执行时间不变,优化的是处理并发的能力和你的商店(服务器)的吞吐量。看代码就明白,异步编程应该应用于IO密集型场景,而不是CPU密集型场景。我们都知道线程是由CPU调度的。如果你有一个四核CPU,那么你的线程池中有四个线程,当进程的每个虚拟CPU分配一个线程时,性能将是最好的。它可以有效地使用CPU,而不会因为来回切换上下文而损失性能。想一想,在CPU密集型的场景下,CPU就是想占着你的线程,这时候异步编程是没有用的。但是在IO场景下,文件IO是从win32用户态API转到windows内核态,磁盘驱动在内核态运行。在此期间,您的线程在驱动程序的响应中被阻塞。在异步编程中,当你的操作被通知给磁盘驱动程序后,线程会立即返回,而不是等待。在未来某个时刻,驱动程序完成处理,处理结果放入CLR线程池队列中,状态机恢复。线程池方法中任意一个线程取到结果,方法继续向下执行。在网络IO中也是如此,只是驱动变成了网络驱动。请看下面的代码:publicstaticasyncTaskDoSomeAsync(){using(varclient=newHttpClient()){varresult=awaitclient.GetAsync("http://stackoverflow.com/questions/37991851/jenkins-configure-page-not-loading-version1-651-3-chrome-browser").Result.Content.ReadAsStringAsync();Console.WriteLine(result);//做一些其他操作varres=1+1;//-----------------return"";}}编译的时候,DosomeAsync会被编译成一个状态机方法,不管状态机是什么,你都可以把它当成一个黑盒子。遇到GetAsync时,在DoSomeAsync中返回一个Task任务对象,恢复状态机的方法通过await传递给Task对象,相当于调用ContinueWith()。顾名思义,这个方法继续xxx。然后该线程从DoSomeAsync返回。回来后干嘛去了?线程可以去其他事情。在未来的某个时刻,服务器向我们发送响应,网络驱动程序知道请求已完成,恢复该方法并继续执行其余代码。拥有杂乱图的额外好处是应用程序的所有线程在GC的垃圾清理期间将被挂起。使用异步编程意味着在相同的并发量下,可以使用更少的线程来完成处理,额外的好处是需要清理的线程更少。还有一点就是使用的线程变少了,CPU线程切换变少了。