异步编程

同时我们会做一个async和await在WinForm中的尝试

概述

  在事先写的一篇关于async和await的前生今生的篇章今后,我们就像在async和await提升网站拍卖技艺方面还会有一对疑云,今日头条自身也做了成都百货上千的尝试。前些天大家再来回答弹指间以此标题,相同的时候我们会做三个async和await在WinForm中的尝试,何况相比在4.5事先的异步编制程序方式APM/EAP和async/await的分别,最终大家还大概会追究在区别线程之间互相的难题。

  IIS存在着处理技术的主题素材,可是WinForm却是UI响应的题目,何况WinForm的UI线程至始至终都以同三个,所以两者之间有早晚的分别。有人会问,今后还应该有人写WinForm吗?好啊,它确是一个比较老的事物吗,比不上WPF炫,技艺也比不上WPF先进,可是从架构层面来说,不管是Web,依旧WinForm,又恐怕WPF,Mobile,那些都只是表现层,不是么?未来的特大型系统日常桌面客商端,Web端,手提式无线电话机,平板端都会涉及,那也是干什么会有应用层,服务层的留存。我们在这评论的ASP.NET
MVC,WinForm,WFP,Android/IOS/WP
都以表现层,在表现层大家相应只管理与“表现”相关的逻辑,任何与职业相关的逻辑应该都是位于下层管理的。关于架构的难点,我们后边再稳步深切,其余别说笔者从没提示您,我们今日还有恐怕会看见.NET中另一个早已老去的能力Web
Service。

  还得唤醒您,作品内容有点长,涉及的知识点非常多,所以,小编引入:”先顶后看“
,先顶后看是21世纪看长篇的首荐之道,是上好关系的起来,想精晓是如何会让您至极吧?想通晓为啥新加坡今天会下如此大的雨么?请牢记先顶后看,你顶的不是自身的篇章,而是大家冒着中雨还要去上班的可贵精神!先顶后看,你值得全数!

目录

async/await如何进级IIS管理技艺

  首先响应手艺并不完全部是说我们前后相继品质的标题,有的时候候大概您的主次尚未别的难题,何况稳重经过优化,不过响应手艺只怕不曾上来,网址品质深入分析是二个错综相连的活,有时候只可以靠经验和持续的尝试才干达到规定的标准比较好的功力。当然大家明天商讨的严重性是IIS的拍卖工夫,或许也说不定便是IIS的习性,但向来不代码本身的本性。即便async/await能够升高IIS的拍卖技艺,可是对于客户来讲一切页面从发起呼吁到页面渲染实现的这么些时间,是不会因为我们加了async/await之后发生多大变迁的。

  其他异步的ASP.NET并不是独有async/await才得以做的,ASP.NET在Web
Form时代就曾经有异步Page了,包涵ASP.NET
MVC不是也可以有异步的Controller么?async/await
很新,很酷,不过它也只是在本来一能力基础上做了有个别改良,让工程师们写起异步代码来更易于了。大家常说微软欣赏新瓶装旧酒,最少我们要看看那一个新瓶给我们带来了怎么着,不管是任何产品,都不只怕一开始就很圆满,所以持续的迭代革新,也可以说是一种科学做事的法子。

ASP.NET并行管理的步骤

   ASP.NET是怎样在IIS中劳作的一文已经很详细的介绍了八个诉求是何等从客商端到服务器的HTTP.SYS最后步向CL昂科拉实行管理的(生硬提出不打听这一块的同窗先看那篇文章,有协理你知道本小节),可是具有的步骤都以依靠三个线程的假设下进展的。IIS本人正是三个二十八线程的干活条件,倘若大家从八线程的见解来看会发生什么样变动呢?大家先是来看一下上面那张图。注意:大家上面包车型地铁步调是确立在IIS7.0现在的三合一形式基础之上的。(注:上边那张图在dudu的提醒之后,重新做了部分搜寻专门的学问,做了某些转移,w3dt这一步来自于今日头条协会对难点的接踵而至 蜂拥而至查究,详细情形能够点这里

澳门蒲京 1

  我们再来梳理一下地方的步骤:

  1. 负有的伸手最起初是由HTTP.SYS接收的,HTTP.SYS内部有多少个行列维护着那一个央浼,那个队列的request的数据超出一定数量(默许是一千)的时候,HTTP.SYS就能一贯回到503场馆(服务器忙),那是我们的首先个阀门。品质计数指标:“Http
    Service Request Queues\CurrentQueueSize
  2. 由w3dt担当把央浼从HTTP.SYS
    的行列中放置三个应和端口的队列中,据非官方资料体现该队列长度为能为20(该队列是非公开的,未有文书档案,所以也尚未质量计数器)。
  3. IIS
    的IO线程从上一步的系列中获得央浼,如若是内需ASP.NET管理的,就能传送给CL昂科雷线程池的Worker 线程,IIS的IO线程继续回来重新做该手续。CL哈弗线程池的Worker线程数量是第二个阀门。
  4. 当CLENVISION中正在被拍卖的呼吁数据超越一定值(最大并行管理伏乞数量,.NET4过后暗中同意是6000)的时候,从IO线程过来的央求就不会直接付出Worker线程,而是放到三个进度池等级的二个行列了,等到这些数额低于临界值的时候,才会把它再也提交Worker线程去管理。那是大家的第八个阀门。
  5. 上一步中提起的非常进度池级其余行列有三个尺寸的限量,能够因此web.config里面包车型客车processModel/requestQueueLimit来安装。那能够说也是一个阀门。当正在管理的多寡超过所允许的最大并行管理须求数量的时候,我们就能够得到503了。能够由此性能计数目的:“ASP.NET
    v4.0.30319\Requests Queued
    ” 来查看该队列的长度。

 哪些因素会操纵我们的响应本事

  从地方大家提到了几大阀门中,我们能够得出上边包车型客车多少个数字调整可能说影响着大家的响应手艺。

  1. HTTP.SYS队列的尺寸
  2. CL中华V线程池最大Worker线程数量
  3. 最大并行管理央浼数量
  4. 进度池等级队列所允许的长短

HTTP.SYS队列的长度

  其一小编感觉无需额外解释,私下认可值是一千。那一个值决定于大家我们后边IIS
IO线程和Worker线程的管理速度,若是它们多个都管理不了,这些数字再大也不曾用。因为最终他们会被积存到进程池等级的行列中,所以只会变成内部存款和储蓄器的荒芜。

最大Worker线程数量

  那一个值是足以在web.config中开展示公布局的。

澳门蒲京 2

  maxWorkerThreads: CLRubicon中实际管理供给的最大Worker线程数量
  minWorkerThreads:CL路虎极光中实际处理诉求的不大Worker线程数量

  minWorkerThreads的暗中认可值是1,合理的加大他们得以制止不须要的线程创造和销毁职业。

最大并行管理央求数量

  进度池等级的队列给大家的CLPAJERO一定的缓冲,那其间要当心的是,这一个行列还并未有步向到CL奥德赛,所以它不会据有大家托管遭遇的别的国资本源,也正是把需要卡在了CLSportage的外面。大家须要在aspnet.config等第进行布置,大家能够在.net
fraemwork的安装目录下找到它。平常是 C:\Windows\Microsoft.NET\Framework\v4.0.30319
要是您安装的是4.0的话。

澳门蒲京 3

  maxConcurrentRequestPerCPU:
各个CPU所允许的最大并行管理要求数量,当CL奇骏中worker线程正在处理的央求之和抢先这么些数时,从IO线程过来的乞请就能够被置于我们进度池等级的行列中。
澳门蒲京,  maxConcurrentThreadsPerCPU: 设置为0即禁用。
  requestQueue: 进度池等第队列所允许的长度  

async和await 做了怎么?

  我们终于要切入主旨了,拿ASP.NET
MVC举个例子,假诺不应用async的Action,那么势必,它是在叁个Woker线程中实行的。当我们拜见一些web
service,只怕读文件的时候,那一个Worker线程就能够被堵塞。假使大家以此Action施行时间累计是100ms,其它访问web
service花了80ms,理想状态下贰个Worker线程一秒能够响应13个央浼,如若大家的maxWorkerThreads是10,那我们一秒内连续可响应诉求就是100。假使说大家想把这么些可响应诉求数升到200如何做吧?

  有人会说,那还不轻易,把maxWorkerThreads调20不就行了么?
其实大家做也未曾怎么
难题,确实是可以的,并且也确实能起到效益。那我们怎么还要挖空心绪的搞什么
async/await呢?搞得脑子都晕了?async/await给大家缓和了哪些难点?它能够在我们访谈web
service的时候把当下的worker线程放走,将它放回线程池,那样它就足以去管理任何的呼吁了。等到web
service给大家重返结果了,会再到线程池中随便拿叁个新的woker线程继续往下进行。也便是说大家收缩了这部分等候的大运,充份利用了线程。

    大家来相比一下选拔async/awit和不应用的情事,

  不行使async/await: 21个woker线程1s得以管理200个央求。

  那调换来总的时间的正是 20 * 1000ms =  20000ms,
  其中等待的时间为 200 * 80ms = 16000ms。
  也正是说使用async/await大家足足节约了1陆仟ms的大运,那21个worker线程又会再去管理哀告,尽管如约每个必要100ms的管理时间大家仍是能够再追加1六19个诉求。并且别忘了100ms是依照联合情形下,富含等待时间在内的功底上获取的,所以实际情状也许还要多,当然大家那边未有算上线程切换的日子,所以其实况况中是有一点距离的,不过应当不会相当大,因为我们的线程都以基于线程池的操作。
  全部结果是贰13个Worker线程不使用异步的动静下,1s能自理200个哀告,而利用异步的气象下得以拍卖3六19个须求,立马升高十分之八呀!采取异步之后,对于同一的恳求数量,须要的Worker线程数据会大大裁减贰分一左右,三个线程至少会在堆上分配1M的内部存款和储蓄器,假诺是一千个线程那就是1G的体积,即使内部存储器今后方便人民群众,可是省着毕竟是好的嘛,何况更加少的线程是足以减小线程池在珍贵线程时发出的CPU消耗的。另:dudu分享 CLXC601秒之内只可以成立2个线程。

  注意:以上数据并不是真实测量试验数据,实际情形叁个request的时刻也休想100ms,开销在web
service上的时刻也不要80ms,仅仅是给我们一个思路:),所以这里面用了async和await之后对响应技巧有多大的升官和大家原先堵塞在这一个IO和网络上的年华是有十分的大的涉及的。

几点提出

  看见这里,不清楚我们有未有获得点什么。首先第一点大家要明白的是async/await不是万能药,不们不能够指望光写五个光键字就期望质量的进级。要牢记,一个CPU在同偶然候段内是不得不施行二个线程的。进而那也是干吗async和await提议在IO只怕是网络操作的时候使用。大家的MVC站点访问WCF可能Web
Service这种现象就可怜的切合利用异步来操作。在上边的例子中80ms读取web
service的岁月,大部份时间都以没有供给cpu操作的,那样cpu才足以被其余的线程利用,如若不是三个读取web
service的操作,而是一个头昏眼花计算的操作,那你就等着cpu爆表吧。

  第二点是,除了程序中接纳异步,大家地点讲到的有关IIS的安插是比较重大的,假诺使用了异步,请记得把maxWorkerThreads和maxConcurrentRequestPerCPU的值调高试试。

 开始时代对Web service的异步编制程序方式APM

  讲罢大家伟大上的async/await之后,大家来看看这么些技艺很老,可是概念确如故一而再于今的Web
瑟维斯。 我们那边所说的对准web
service的异步编制程序方式不是指在劳动器端的web service自身,而是指调用web
service的客商端。大家理解对于web service,大家因此增添web
service引用也许.net提供的变动工具就能够改变对应的代理类,能够让我们像调用本地代码一样访谈web
service,而所生成的代码类中对针对每一个web
service方法生成3个照看的格局,比如说我们的方法名为DownloadContent,除了那个措施之外还应该有BeginDownloadContent和EndDownloadContent方法,而那五个就是大家明日要说的开始时代的异步编制程序形式APM(Asynchronous
Programming Model)。上边就来看看我们web
service中的代码,注意我们今后的门类都以在.NET Framework3.5下促成的。

 PageContent.asmx的代码

public class PageContent : System.Web.Services.WebService
{
    [WebMethod]
    public string DownloadContent(string url)
    {
        var client = new System.Net.WebClient();
        return client.DownloadString(url);
    }
}

  注意我们web
service中的DownloadContent方法调用的是WebClient的联合方法,WebClient也可能有异步方法即:DownloadStringAsync。不过大家要明白,不管服务器是一路照旧异步,对于客户端的话调用了您那些web
service都以同等的,正是得等您回去结果。

  当然,大家也足以像MVC里面包车型地铁代码一样,把大家的劳动器端也写成异步的。那获得好处的是十二分托管web
service的服务器,它的管理技能赢得升高,就如ASP.NET一样。要是大家用JavaScript去调用那些Web
Service,那么Ajax(Asynchronous Javascript +
XML)便是大家客商端用到的异步编制程序本领。若是是其他的顾客端呢?比如说一个CS的桌面程序?大家须要异步编制程序么?

当WinForm遇上Web Service

  WinForm不像托管在IIS的ASP.NET网址,会有贰个线程池管理着多个线程来拍卖客商的呼吁,换个说法ASP.NET网址生来就是基于八线程的。不过,在WinForm中,即使大家不特意使用八线程,那至始至终,都独有二个线程,称之为UI线程。只怕在有的小型的系统中WinForm比相当少涉及到多线程,因为WinForm本身的优势就在它是单身运维在顾客端的,在性质上和可操作性上都会有极大的优势。所以重重中小型的WinForm系统都以间接就访谈数据库了,並且大多也独有多少的传输,什么图片财富那是少之甚少的,所以等待的时刻是极短的,基本不用费怎么样脑子去思量怎么3秒之内必得将页面显示到顾客前面这种难题。

  既然WinForm在品质上有这么大的优势,那它还须求异步吗?

  大家地方说的是中型Mini型的WinForm,假使是巨型的种类啊?倘诺WinForm只是别的的非常的小一些,似乎大家文章起先说的还恐怕有比相当多任何众多少个手提式有线电电话机客商端,Web客商端,平板客商端呢?如若客户端相当多形成数据库撑不住如何是好?
想在中游加一层缓存怎么办?

  拿八个b2b的网址作用举个例子,客商能够由此网址下单,手提式有线电电话机也得以下单,还能够通过Computer的桌面顾客端下单。在下完单之后要到位交易,仓库储存扣减,发送订单确认布告等等成效,而不论是你的订单是因而哪些端完成的,那些效能大家都要去做,对吧?那我们就无法独立放在WinForm里面了,不然那一个代码在任何的端里面又得全部簇新再逐个完毕,同样的代码放在分歧的地点那然则非常惊险的,所以就有了作者们后来的SOA架构,把这一个成效都分红服务,每类别型的端都以调用服务就足以了。一是足以统一珍惜那么些职能,二是可以很便利的做扩张,去更好的适应作用和架构上的扩充。举个例子说像上边那样的贰个类别。

 澳门蒲京 4

  在上海体育场合中,Web端尽管也是属于大家平时说的服务端(以致是由多台服务器组成的web会集),可是对大家任何种类来讲,它也只是二个端而已。对于二个端的话,它自个儿只管理和顾客交互的主题素材,别的全数的效劳,业务都会提交后来台管理。在大家地点的架构中,应用层都不会直接参与真正业务逻辑相关的管理,而是放到我们更下层数据层去做拍卖。那么应用层首要扶助做一些与客商交互的一部分效应,要是手提式有线电话机短信发送,邮件发送等等,并且能够依据优先级选拔是放入队列中稍候管理依旧一向调用功用服务及时管理。

  在如此的叁个种类中,我们的Web服务器能够,Winform端也好都将只是漫天系统中的三个终端,它们主要的任何是客户和前边服务时期的一个桥梁。涉及到Service的调用之后,为了给顾客能够的客户体验,在WinForm端,大家本来就要怀念异步的主题素材。 

WinForm异步调用Web Service

  有了像VS这样强劲的工具为大家转移代理类,咱们在写调用Web
service的代码时就能够像调用本地类库同样调用Web
Service了,大家只须要加上一个Web Reference就能够了。

澳门蒲京 5

// Form1.cs的代码

private void button1_Click(object sender, EventArgs e)
{
    var pageContentService = new localhost.PageContent();
    pageContentService.BeginDownloadContent(
        "http://jesse2013.cnblogs.com",
        new AsyncCallback(DownloadContentCallback),
        pageContentService);
}

private void DownloadContentCallback(IAsyncResult result)
{
    var pageContentService = (localhost.PageContent)result.AsyncState;
    var msg = pageContentService.EndDownloadContent(result);
    MessageBox.Show(msg);
}

  代码特其余简单,在实施完pageContentService.BeginDownloadContent之后,大家的主线程就回来了。在调用Web
service近来内大家的UI不会被封堵,也不会冒出“不能响应这种状态”,我们仍旧能够拖动窗体以致做其余的事务。那正是APM的魅力,但是大家的callback毕竟是在哪个线程中实践的吧?是线程池中的线程么?咋们接着往下看。

APM异步编程格局详解

线程难题

  接下去大家正是更上一层楼的刺探APM这种形式是如何做事的,但是首先大家要回应上边留下来的主题素材,这种异步的编制程序格局有未有为大家张开新的线程?让代码说话:

private void button1_Click(object sender, EventArgs e)
{
    Trace.TraceInformation("Is current thread from thread pool? {0}", Thread.CurrentThread.IsThreadPoolThread ? "Yes" : "No");
    Trace.TraceInformation("Start calling web service on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    var pageContentService = new localhost.PageContent();
    pageContentService.BeginDownloadContent(
        "http://jesse2013.cnblogs.com",
        new AsyncCallback(DownloadContentCallback),
        pageContentService);
}

private void DownloadContentCallback(IAsyncResult result)
{
    var pageContentService = (localhost.PageContent)result.AsyncState;
    var msg = pageContentService.EndDownloadContent(result);

    Trace.TraceInformation("Is current thread from thread pool? {0}" , Thread.CurrentThread.IsThreadPoolThread ? "Yes" : "No");
    Trace.TraceInformation("End calling web service on thread: {0}, the result of the web service is: {1}",
        Thread.CurrentThread.ManagedThreadId,
        msg);
}

  大家在开关点击的法子和callback方法中分别出口当前线程的ID,以及她们是否属于线程池的线程,获得的结果如下:

  Desktop4.0.vshost.exe
Information: 0 : Is current thread a background thread? NO
  Desktop4.0.vshost.exe
Information: 0 : Is current thread from thread pool? NO
  Desktop4.0.vshost.exe
Information: 0 : Start calling web service on thread: 9
  Desktop4.0.vshost.exe
Information: 0 : Is current thread a background thread? YES
  Desktop4.0.vshost.exe
Information: 0 : Is current thread from thread pool? YES
  Desktop4.0.vshost.exe
Information: 0 : End calling web service on thread: 14, the result of
the web service is: <!DOCTYPE html>…

  开关点击的情势是由UI直接决定,很鲜明它不是三个线程池线程,亦不是后台线程。而作者辈的callback却是在三个来源于于线程池的后台线程实施的,答案公布了,不过那会给大家带来贰个主题素材,我们地点讲了独有UI线程也得以去立异我们的UI控件,也即是说在callback中大家是无法更新UI控件的,那我们怎么样让更新UI让用户知道反馈呢?答案在前边接晓
:),让大家先注意于把APM弄领会。

从Delegate开始

  其实,APM在.NET3.5在此在此以前都被周边运用,在WinForm窗体调控中,在二个IO操作的类库中等等!大家能够很轻松的找到搭配了Begin和End的措施,更主要的是假设是有代理的地点,大家都得以行使APM这种形式。大家来看八个非常的粗略的例证:

delegate void EatAsync(string food);
private void button2_Click(object sender, EventArgs e)
{
    var myAsync = new EatAsync(eat);
    Trace.TraceInformation("Activate eating on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    myAsync.BeginInvoke("icecream", new AsyncCallback(clean), myAsync);
}

private void eat(string food)
{
    Trace.TraceInformation("I am eating.... on thread: {0}", Thread.CurrentThread.ManagedThreadId);
}

private void clean(IAsyncResult asyncResult)
{
    Trace.TraceInformation("I am done eating.... on thread: {0}", Thread.CurrentThread.ManagedThreadId);
}

  上边包车型大巴代码中,我们通过把eat封装成贰个委托,然后再调用该信托的BeginInvoke方法达成了异步的实践。也正是实际上的eat方法不是在主线程中推行的,大家得以看输出的结果:

  Desktop4.0.vshost.exe
Information: 0 : Activate eating on thread: 10
  Desktop4.0.vshost.exe
Information: 0 : I am eating…. on thread: 6
  Desktop4.0.vshost.exe
Information: 0 : I am done eating…. on thread: 6

  clean是大家传进去的callback,该方法会在大家的eat方法试行完事后被调用,所以它会和大家eat方法在同一个线程中被调用。大家如若熟识代理的话就能驾驭,代码实际上会被编写翻译成叁个类,而BeginInvoke和EndInvoke方法正是编写翻译器为大家机关加进去的不二诀要,大家不用额外做其他业务,那在中期未有TPL和async/await以前(APM从.NET1.0有的时候就有了),的确是八个不错的挑选。

再度认知APM

通晓了Delegate完毕的BeginInvoke和EndInvoke之后,大家再来深入分析一下APM用到的那多少个对象。
拿我们Web service的代办类来举例,它为大家转移了以下3个法子:

  1. string DownloadContent(string url): 同步方法
  2. IAsyncResult BeginDownloadContent(string url, AsyncCallback
    callback, object asyncState): 异步初叶方法
  3. EndDownloadContent(IAsyncResult asyncResult):异步甘休方法

  在大家调用EndDownloadContent方法的时候,倘诺大家的web

service调用还未曾回来,这那个时候就能够用阻塞的法门去拿结果。可是在我们传到BeginDownloadContent中的callback被调用的时候,那操作必然是曾经实现了,也正是说IAsyncResult.IsCompleted

true。而在APM异步编制程序情势中Begin方法总是回到IAsyncResult那些接口的兑现。IAsyncReuslt仅仅富含以下4个属性:

澳门蒲京 6

  WaitHanlde日常作为一道对象的基类,何况能够动用它来阻塞线程,更加多音信能够参照MSDN.aspx) 。
借助于IAsyncResult的帮带,我们就足以由此以下三种格局去取安妥前所实行操作的结果。

  1. 轮询
  2. 强制等待
  3. 完了文告

  落成通告正是们在”WinForm异步调用WebService”这结中用到的秘籍,调完Begin方法之后,主线程纵然达成义务了。我们也不用监督该操作的实施景况,当该操作施行完事后,我们在Begin方法中传进去的callback就能够被调用了,大家能够在万分方式中调用End方法去获取结果。上面我们再轻松说一下前方三种方法。

//轮询获取结果代码

var pageContentService = new localhost.PageContent();
IAsyncResult asyncResult = pageContentService.BeginDownloadContent(
    "http://jesse2013.cnblogs.com",
    null,
    pageContentService);

while (!asyncResult.IsCompleted)
{
    Thread.Sleep(100);
}
var content = pageContentService.EndDownloadContent(asyncResult);

 // 强制等待结果代码

var pageContentService = new localhost.PageContent();
IAsyncResult asyncResult = pageContentService.BeginDownloadContent(
    "http://jesse2013.cnblogs.com",
    null,
    pageContentService);

// 也可以调用WaitOne()的无参版本,不限制强制等待时间
if (asyncResult.AsyncWaitHandle.WaitOne(2000))
{
    var content = pageContentService.EndDownloadContent(asyncResult);
}
else
{ 
    // 2s时间已经过了,但是还没有执行完   
}

EAP(Event-Based Asynchronous Pattern)

  EAP是在.NET2.0生产的另一种过渡的异步编制程序模型,也是在.NET3.5以往Microsoft扶助的一种做法,为何吗?
假诺大家建一个.NET4.0依然越来越高版本的WinForm项目,再去增多Web
Reference就能意识变化的代理类中早已远非Begin和End方法了,记住在3.5的时候是四头共存的,你能够选拔随机一种来利用。不过到了.NET4.0从此,EAP成为了你独一的挑三拣四。(作者从没尝试过手动生成代理类,风野趣的同室能够品尝一下)让大家来看一下在.NET4下,大家是怎么样异步调用Web
Service的。

private void button1_Click(object sender, EventArgs e)
{
    var pageContent = new localhost.PageContent();
    pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");
    pageContent.DownloadContentCompleted += pageContent_DownloadContentCompleted;
}

private void pageContent_DownloadContentCompleted(object sender, localhost.DownloadContentCompletedEventArgs e)
{
    if (e.Error == null)
    {
        textBox1.Text = e.Result;
    }
    else
    { 
        // 出错了
    }
}

线程难题

  不精晓大家照旧否记得,在APM形式中,callback是推行在另贰个线程中,无法随易的去更新UI。不过一旦您细心看一下方面的代码,大家的DownloadContentCompleted事件绑定的措施中一贯就立异了UI,把重临的剧情写到了八个文本框里面。通过一致的不二等秘书籍能够窥见,在EAP这种异步编制程序形式下,事件绑定的方法也是在调用的可怜线程中实践的。也正是说解决了异步编制程序的时候UI交互的难点,而且是在同二个线程中举行。
看看下面包车型的士代码:

private void button1_Click(object sender, EventArgs e)
{
    Trace.TraceInformation("Call DownloadContentAsync on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    Trace.TraceInformation("Is current from thread pool? : {0}", Thread.CurrentThread.IsThreadPoolThread ? "YES" : "NO");

    var pageContent = new localhost.PageContent();
    pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");
    pageContent.DownloadContentCompleted += pageContent_DownloadContentCompleted;
}

private void pageContent_DownloadContentCompleted(object sender, localhost.DownloadContentCompletedEventArgs e)
{
    Trace.TraceInformation("Completed DownloadContentAsync on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    Trace.TraceInformation("Is current from thread pool? : {0}", Thread.CurrentThread.IsThreadPoolThread ? "YES" : "NO");
}

  Desktop4.vshost.exe
Information: 0 : Call DownloadContentAsync on thread: 10

  Desktop4.vshost.exe
Information: 0 : Is current from thread pool? : NO

  Desktop4.vshost.exe
Information: 0 : Completed DownloadContentAsync on thread: 10

  Desktop4.vshost.exe
Information: 0 : Is current from thread pool? : NO

async/await 给WinFrom带来了哪些

  若是说async给ASP.NET带来的是拍卖本领的增长,那么在WinForm中给技术员带来的平价则是最大的。大家再也绝不因为要落到实处异步写回调只怕绑定事件了,省事了,可读性也加强了。不信你看上面大家将调用我们十二分web
service的代码在.NET4.5下落成一下:

private async void button2_Click(object sender, EventArgs e)
{
    var pageContent = new localhost.PageContentSoapClient();
    var content = await pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");

    textBox1.Text = content.Body.DownloadContentResult;
}

  轻巧的三行代码,像写同步代码同样写异步代码,作者想也许这正是async/await的吸重力吧。在await之后,UI线程就能够回去响应UI了,在上头的代码中大家是绝非新线程发生的,和EAP同样获得结果直接就足以对UI操作了。

  async/await就如真正很好,但是倘若我们await前面的代码实施在其他三个线程中会发生哪些职业吗?

private async void button1_Click(object sender, EventArgs e)
{
    label1.Text = "Calculating Sqrt of 5000000";
    button1.Enabled = false;
    progressBar1.Visible = true;

    double sqrt = await Task<double>.Run(() =>
    {
        double result = 0;
        for (int i = 0; i < 50000000; i++)
        {
            result += Math.Sqrt(i);

            progressBar1.Maximum = 50000000;
            progressBar1.Value = i;
        }
        return result;
    });

    progressBar1.Visible = false;
    button1.Enabled = true;
    label1.Text = "The sqrt of 50000000 is " + sqrt;
}

  大家在分界面中放了叁个ProgressBar,同不经常候开贰个线程去把从1到6000000的平方全体加起来,看起来是一个要命耗费时间的操作,于是我们用Task.Run开了八个新的线程去施行。(注:假如是纯运算的操作,三四线程操作对品质未有多大扶助,我们那边最首假使想给UI贰个速度展现当前进展到哪一步了。)看起来未有何样难点,我们按F5运作吧!
  Bomb~

澳门蒲京 7

  当实践到此地的时候,程序就咽气了,告诉我们”无效操作,只好从创制porgressBar的线程访谈它。“
 那也是大家一开端波及的,在WinForm主次中,独有UI主线程本领对UI进行操作,其余的线程是不曾权限的。接下来我们就来看看,就算在WinForm中落到实处非UI线程对UI调整的更新操作。 

分歧线程之间通信的标题

万能的Invoke

  WinForm中相当多的控件富含窗体在内都完毕了Invoke.aspx)方法,能够流传八个Delegate,那一个Delegate将会被抱有非常调整的线程所调用,进而幸免了跨线程访谈的标题。

Trace.TraceInformation("UI Thread : {0}", Thread.CurrentThread.ManagedThreadId);
double sqrt = await Task<double>.Run(() =>
{
    Trace.TraceInformation("Run calculation on thread: {0}", Thread.CurrentThread.ManagedThreadId);
    double result = 0;
    for (int i = 0; i < 50000000; i++)
    {
        result += Math.Sqrt(i);
        progressBar1.Invoke(new Action(() => {
            Trace.TraceInformation("Update UI on thread: {0}", Thread.CurrentThread.ManagedThreadId);
            progressBar1.Maximum = 50000000;
            progressBar1.Value = i;
        }));
    }
    return result;
});

  Desktop.vshost.exe Information: 0 : UI
Thread : 9
  Desktop.vshost.exe Information: 0 : Run calculation on
thread: 10
  Desktop.vshost.exe
Information: 0 : Update UI on thread: 9

  Invoke方法相比轻便,大家就不做过多的钻研了,不过大家要挂念到有些,Invoke是WinForm达成的UI跨线程交换格局,WPF用的却是Dispatcher,若是是在ASP.NET下跨线程之间的一齐又怎么办呢。为了合作各种技艺平台下,跨线程同步的主题材料,Microsoft在.NET2.0的时候就引入了我们上面包车型大巴这几个目的。

SynchronizationContext上下文同步对象

怎么须要SynchronizationContext

  就好像我们在WinForm中相见的标题同样,不时候大家供给在三个线程中传送一些数据依然做一些操作到另叁个线程。可是在多数动静下那是不容许的,出于安全因素的虚构,每三个线程都有它独自的内部存款和储蓄器空间和上下文。由此在.NET2.0,微软推出了SynchronizationContext。

  它首要的功用之一是为大家提供了一种将某个工作职分(Delegate)以队列的艺术存款和储蓄在二个上下文对象中,然后把这个上下文对象关系到实际的线程上,当然有的时候多少个线程也得以提到到同一个SynchronizationContext对象。获取当前线程的联合签名上下文对象足以行使SynchronizationContext.Current。同一时间它还为大家提供以下五个措施Post和Send,分别是以异步和协助进行的办法将我们地点说的行事任务放到大家SynchronizationContext的行列中。

SynchronizationContext示例

  如故拿我们地方Invoke中用到的例子比如,只是本次大家不直接调用控件的Invoke方法去立异它,而是写了三个Report的办法律专科高校门去更新UI。

double sqrt = await Task<double>.Run(() =>
{
    Trace.TraceInformation("Current thread id is:{0}", Thread.CurrentThread.ManagedThreadId);

    double result = 0;
    for (int i = 0; i < 50000000; i++)
    {
        result += Math.Sqrt(i);
        Report(new Tuple<int, int>(50000000, i));
    }
    return result;
});

  每三遍操作完事后大家调用一下Report方法,把我们一齐要算的数字,以及当前正值测算的数字传给它就足以了。接下来就看我们的Report方法了。

private SynchronizationContext m_SynchronizationContext;
private DateTime m_PreviousTime = DateTime.Now;

public Form1()
{
    InitializeComponent();
    // 在全局保存当前UI线程的SynchronizationContext对象
    m_SynchronizationContext = SynchronizationContext.Current;
}

public void Report(Tuple<int, int> value)
{
    DateTime now = DateTime.Now;
    if ((now - m_PreviousTime).Milliseconds > 100)
    {
        m_SynchronizationContext.Post((obj) =>
        {
            Tuple<int, int> minMax = (Tuple<int, int>)obj;
            progressBar1.Maximum = minMax.Item1;
            progressBar1.Value = minMax.Item2;
        }, value);

        m_PreviousTime = now;
    }
}

  整个操作看起来要比Inovke复杂一点,与Invoke不一样的是SynchronizationContext没有供给对Control的援用,而Invoke必得先得有那几个控件才干调用它的Invoke方法对它实行操作。

小结

  那篇博客内容有一些长,不知情有微微人方可知到此间:)。最最初本身只是想写写WinFrom下异步调用Web
Service的局地东西,在一开首那篇文书的难题是”异步编制程序在WinForm下的实行“,然而写着写着发掘更是多的迷团未有解开,其实都以一对老的本领在此之前并未有接触和垄断(monopoly)好,所以所幸就二遍性把他们都重新学习了三次,与我们大快朵颐。

  我们再来回看一下作品所涉嫌到的一对重要的概念:

  1. async/await
    在ASP.NET做的最大进献(开始的一段时代ASP.NET的异步开辟情势同样也可能有那样的贡献),是在访谈数据库的时候、访谈远程IO的时候立时放出了当下的管理性程,可以让那几个线程回到线程池中,进而达成能够去管理其余须求的作用。
  2. 异步的ASP.NET开荒能够在拍卖本领上带来多大的增高,决定于大家的主次有多少时间是被堵塞的,也正是那二个访谈数据库和长途Service的时日。
  3. 除去将代码改成异步,我们还亟需在IIS上做一些相对的铺排来贯彻最优化。
  4. 甭管是ASP.NET、WinForm还是Mobile、还是平板,在大型系统中都只是一个与顾客交互的端而已,所以不管您今后是做所谓的前端(JavaScript +
    CSS等),依旧所谓的后端(ASP.NET MVC、WCF、Web API 等
    ),又只怕是相比新颖的移动端(IOS也好,Andrioid也罢,哪怕是不争气的WP),都只是任何大型系统中的零星一角而已。当然笔者并不是降级这么些端的价值,就是因为我们注意于分化,努力进步每一个端的客户体验,能力让这么些大型系统有露脸的机遇。小编想说的是,在你对前些天手艺取得一定的达成之后,不要甘休学习,因为整个软件架构种类中还会有不菲过多妙不可言的东西值得大家去开掘。
  5. APM和EAP是在async/await以前的三种差别的异步编制程序方式。
  6. APM如若不打断主线程,那么成功通知(回调)就能够试行在别的一个线程中,进而给我们更新UI带来一定的题目。
  7. EAP的通告事件是在主线程中试行的,不会设有UI交互的主题素材。
  8. 聊起底,大家还学习了在Winform下分裂线程之间相互的标题,以及SynchronizationContext。
  9. APM是.NET下最初的异步编制程序方法,从.NET1.0以来就有了。在.NET2.0的时候,微软察觉到了APM的回调函数中与UI交互的标题,于是带来了新的EAP。APM与EAP一向并存到.NET3.5,在.NET4.0的时候微软带来了TPL,也正是大家所熟练的Task编制程序,而.NET4.5正是大家我们领会的async/await了,能够看见.NET平昔在不停的进步,加上近日不休的和开源社区的搭档,跨平台等特色的引入,我们有理由相信.NET会越走越好。

  最终,那篇文章从找材料学习到写出来,差不离花了本身八个周未的时刻,希望能够给急需的人要么感兴趣想要不断学习的人一点帮助(不管是往前读书,依旧今后学习)最终还要感激@田园里面包车型大巴蟋蟀,在翻阅的时候给自个儿找了有的错别字!

引用 & 扩充阅读

http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
http://blog.stevensanderson.com/2008/04/05/improve-scalability-in-aspnet-mvc-using-asynchronous-requests
http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx 
http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
http://mohamadhalabi.com/2014/05/08/thread-throttling-in-iis-hosted-wcf-sync-vs-async/
Pro Asynchronous Programs with .NET by Richard Blewett and Andrew Clymer