正确运用异步操作51CTO博客 - 亚美娱乐

正确运用异步操作51CTO博客

2019-01-04 21:32:09 | 作者: 运锋 | 标签: 操作,异步,运用 | 浏览: 113

版权声明:原创著作,答应转载,转载时请有必要以超链接办法标明文章原始出处(以下两个链接可任选其一)、作者信息和本声明。不然将追查法律责任。
  • [url]http://jeffreyzhao.cnblogs.com/archive/2008/02/24/use-async-operation-properly.html[/url]
  本想写一点有关LINQ to SQL异步调用的论题,可是在这之前我想仍是先写一篇文章来论述一下运用异步操作的一些准则,防止有些朋友误用导致程序功能反而下降。这篇文章会讨论一下在.NET中有关异步操作论题,从理论动身结合实践,以弄清概念及防止误用为方针,而且最终提出常见的异步操作场景和运用事例。这样咱们就能够知道什么时分该运用异步操作,什么时分会因小失大。   那么咱们先来承认一个概念,那便是“线程”。请留意,假如没有特别阐明,本文中呈现的“线程”所指的是CLR线程池(Thread Pool)中的保管线程,它和Windows线程或纤程(fiber)并不是同一个的概念。相同,它也不是指System.Thread类的实例。简略地说,它是由CLR办理的作业履行单元,每逢需求履行使命时,CLR就会分配一个这样的履行单元去作业。当一切的线程池内的线程都用完之后就无法履行新的使命了,一个保管线程在使命完结之后被开释停止。线程池自身是一个“目标池”,会在需求新目标(保管线程)时创立,而在目标不需求之后(一段特定时刻之内没有新使命需求分配保管线程)担任毁掉以开释资源。至于线程池的线程数量,在CLR 2.0 SP1之前的版别中是CPU数 * 25,不过从CLR 2.0 SP1之后就变成了CPU数 * 250。不过不论怎样样,线程池内的线程是有限的,咱们有必要合理地运用它。   从前的核算机只需一个CPU,理论上同一时刻只能履行一个使命。而现在的超线程、多核、乃至是真实的多个CPU都使核算机能够一同运转多个使命。多线程编程的一个重要特色便是能够充分运用CPU的运算才干,更快地完结某个使命。很明显,假如一个十分巨大的核算使命只交由一个线程来完结,那么只能让一个CPU参加运算。可是假如将一个大使命拆分红多个互不影响的子使命,那么就能让多个CPU一同参加运算,所花的时刻天然就少了。假如某个操作的意图是进行很多运算,或许说需求花费很多时刻运算上的操作,咱们将其称作“Compute-Bound Operation”,也便是受运算才干约束的操作。   与“Compute-Bound Operation”相对的则是“IO-Bound Operation”。“IO-Bound Operation”是指那些由于遭到外部条件约束,完结这样一个使命需求在IO上花费很多时刻的操作。例如读取一个文件,或许恳求网络上的某个资源。关于这种操作,核算的线程再多,运算才干再强也杯水车薪,由于使命遭到的是硬盘、网络等IO设备带来的约束。关于IO-Bound Operation,咱们能做的只需“等候”。   关于“同步操作”来说,“等候”就意味着“堵塞”,一个线程将会“无所事事”直至操作完结。这种做法在许多时分会带来各种问题,因而就呈现了“异步操作”,可是相同是“异步操作”,不同的使命,不同的状况,它解决问题的办法和带来的作用也是不同的。我下面就经过日子中的实例来阐明这些内容:   老赵的朋友开了一家饭馆,请了10个作业人员。最近那个朋友经常向老赵诉苦,说作业人员人手总是不行,在客人比较多的时分,总是来不及款待他们。老赵一问才得知,这家饭馆的作业办法比较特别:当客人来用餐时,就会有作业人员迎上去热心款待,当客人点好菜之后,作业人员就会去进入厨房亲身下厨——没错,便是这样——做完之后,作业人员会将饭菜端至客人面前,然后就去款待其他客人。由于烧菜往往需求很长时刻,因而在某些时分就会发现一切的作业人员都在厨房,可是却没有人点菜。所以老赵给朋友出了个主见:让几个作业人员作为服务员,只担任款待客人,剩余的就当厨师,一直在厨房作业。当客人点菜之后,服务员就把客人的需求告诉厨师,厨师开端作业,而服务员就能够去款待其他客人了。朋友彻悟,问题就这样方便的解决了。   当然,上面故事中老赵的朋友真实太笨,现实日子中的饭馆老板都不会犯这种人员调度上的初级失误。开发一个客户端应用程序所遇到的状况往往就和以上的状况相似。在运转程序时,UI线程(服务员)担任显现界面(款待客人),当用户操作应用程序(点菜)之后,UI线程能够运用同步操作进行运算(服务员亲身下厨),可是假如这是个长时刻的Compute-Bound Operation(烧菜是个花费人手时刻较长的操作),界面就无法重绘或呼应用户恳求了(无法款待客人了),这样的应用程序用户体会天然欠好(客人觉得服务质量低下)。可是只需UI线程运用异步操作(告诉厨师),让另一个线程(另一个作业人员)来进行运算,UI线程就能够持续担任界面重绘或许其他用户操作(款待其他客人)了。   在这种的状况下,异步操作并没有进步运算才干或许节约资源(仍是需求一个人员的作业),可是供给了较好的用户体会。不过咱们这时该怎样运用异步操作呢?在实践开发中,咱们能够运用托付的BeginInvoke进行异步调用。   下面的比如则对应了另一种状况:   老赵的那个开饭馆的朋友在小赚一笔之后预备再开一家快餐店。快餐店和饭馆有个不同之处,那便是快餐店的食物出产了大都有机器完结。惋惜在这种状况下那个朋友仍是遇到了问题:机器数量捉襟见肘,可是人手仍是不行。本来现在的做法仍是适当不科学:服务员知道客人需求的食物之后,就将质料塞入机器,并看着机器是如何将质料变为甘旨的。当机器的作业完结之后,服务员便将食物打包并送出,然后持续款待其他客人。老赵听后仍是哭笑不得:为啥服务员不能在机器作业的时分就去款待其他客人呢?   与这个示例对应的能够是一个ASP.NET应用程序。在ASP.NET中每个恳求(客人)都会运用一个线程池内的线程(服务员)来处理(款待),处理中很或许需求拜访数据库(运用机器),关于一般的做法,处理线程会等候数据库操作回来(服务员看着机器直至完结)。关于Web服务器来说,这很或许是个长时刻的IO-Bound Operation,假如线程长时刻被堵塞很或许就会下降Web应用程序的功能,由于线程池里的线程用完之后(服务员都去看炉子了),就无法处理新的恳求了(没人款待客人了)。假如咱们能够在数据库进行长时刻查询操作时,让线程去处理其他的恳求(款待其他客人)。这样,咱们只需求在数据库操作完结之后持续处理(打包)并将数据发送给客户端(送出)即可。   这便是处理IO-Bound Operation的办法,很明显,这也是一个异步操作。当咱们期望进行一个异步的IO-Bound Operation时,CLR会(经过Windows API)向设备(例如硬盘)宣布IRP(I/O Request Packet)。当设备好预备处理恳求时,就会找出一个它“最想处理”的IRP(例如一个读取离当时磁头最近的数据的恳求)并进行处理,处理完毕后设备将会交还一个表明作业完结的IRP。CLR会为每个进程创立一个IOCP(I/O Completion Port)并和Windows操作系统一同保护。IOCP中一旦被放入表明完结的IRP之后(经过内部的ThreadPool.BindHandle完结),CLR就会赶快分配一个可用的线程用于持续接下去的使命。   这种做法的需求一个重要条件,这便是宣布用于恳求的IRP的操作能够当即回来,而且这个IO操作不会运用任何线程。而此刻,这种异步调用是真实地在节约资源,由于咱们能够腾出线程用来处理其他使命了,这便是和第一种异步调用的最大差异。不过很惋惜,这种做法明显需求操作系统和设备的支撑,也便是只需特定的操作才干享用这些待遇。那么.NET Framework中哪些操作能从中获利呢?
  • FileStream操作:BeginRead、BeginWrite。调用BeginRead/BeginWrite时会建议一个异步操作,可是只需在创立FileStream时传入FileOptions.Asynchronous参数才干获取真实的IOCP支撑,不然BeginXXX办法将会运用默许界说在Stream基类上的完成。Stream基类中BeginXXX办法会运用托付的BeginInvoke办法来建议异步调用——这会运用一个额定的线程来履行使命。尽管当时调用线程当即回来了,可是数据的读取或写入操作仍旧占用着另一个线程(IOCP支撑的异步操作时不需求线程的),因而并没有任何“节约”,反而还很有或许下降了应用程序的功能,由于额定的线程切换会形成功能丢失。
  • DNS操作:BeginGetHostByName、BeginResolve。
  • Socket操作:BeginAccept、BeginConnect、BeginReceive等等。
  • WebRequest操作:BeginGetRequestStream、BeginGetResponse。
  • SqlCommand操作:BeginExecuteReader、BeginExecuteNonQuery等等。这或许是开发一个Web应用时最常用的异步操作了。假如需求在履行数据库操作时得到IOCP支撑,那么需求在衔接字符串中符号Asynchronous Processing为true(默许为false),不然在调用BeginXXX操作时就会抛出反常。
  • WebServcie调用操作:例如.NET 2.0或WCF生成的Web Service Proxy中的BeginXXX办法、WCF中ClientBase<TChannel>的InvokeAsync办法。
  有一点我想再着重一下,那便是托付的BeginInvoke办法并不能取得IOCP支撑,这会运用一个额定的线程来履行使命,这样不光没有节约,返而会下降功能。还有一点或许需求留意,IOCP确实能够不占用线程,可是一个真实的异步操作也不能毁在咱们的代码中。例如我从前看到过如下的代码: SqlCommand command;

IAsyncResult ar = command.BeginExecuteNonQuery();
int result = command.EndExecuteNonQuery(ar);   尽管在调用BeginExecuteNonQuery办法之后确实取得了IOCP的支撑,可是之后调用的EndExecuteNonQuery却会堵塞当时线程直至数据库操作回来——异步操作不是这样用的。至于正确的做法,网络上已经有不少文章叙述了如安在ASP.NET中正确运用异步操作,我们能够查找相应的材料来看,我也会在今后的文章中略有说到。   关于异步操作,这次就讲到这儿吧。
版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表亚美娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章