成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

異步編程In .NET:APM/EAP和async/await

開(kāi)發(fā) 后端
本文將解讀大家在async和await提高網(wǎng)站處理能力方面還有一些疑問(wèn),同時(shí)我們會(huì)做一個(gè)async和await在WinForm中的嘗試,并且對(duì)比在4.5之前的異步編程模式APM/EAP和async/await的區(qū)別,最后我們還會(huì)探討在不同線程之間交互的問(wèn)題。

概述

在之前寫(xiě)的一篇關(guān)于async和await的前世今生的文章之后,大家似乎在async和await提高網(wǎng)站處理能力方面還有一些疑問(wèn),博客園本身也做了不少的嘗試。今天我們?cè)賮?lái)回答一下這個(gè)問(wèn)題,同時(shí)我們會(huì)做一個(gè)async和await在WinForm中的嘗試,并且對(duì)比在4.5之前的異步編程模式APM/EAP和async/await的區(qū)別,***我們還會(huì)探討在不同線程之間交互的問(wèn)題。

IIS存在著處理能力的問(wèn)題,但是WinForm卻是UI響應(yīng)的問(wèn)題,并且WinForm的UI線程至始至終都是同一個(gè),所以兩者之間有一定的區(qū)別。有人會(huì)問(wèn),現(xiàn)在還有人寫(xiě)WinForm嗎?好吧,它確是一個(gè)比較老的東西呢,不如WPF炫,技術(shù)也不如WPF先進(jìn),但是從架構(gòu)層面來(lái)講,不管是Web,還是WinForm,又或是WPF,Mobile,這些都只是表現(xiàn)層,不是么?現(xiàn)在的大型系統(tǒng)一般桌面客戶端,Web端,手機(jī),平板端都會(huì)涉及,這也是為什么會(huì)有應(yīng)用層,服務(wù)層的存在。我們?cè)谶@談?wù)摰腁SP.NET MVC,WinForm,WFP,Android/IOS/WP 都是表現(xiàn)層,在表現(xiàn)層我們應(yīng)該只處理與“表現(xiàn)”相關(guān)的邏輯,任何與業(yè)務(wù)相關(guān)的邏輯應(yīng)該都是放在下層處理的。關(guān)于架構(gòu)的問(wèn)題,我們后面再慢慢深入,另外別說(shuō)我沒(méi)有提示您,我們今天還會(huì)看到.NET中另一個(gè)已經(jīng)老去的技術(shù)Web Service。

還得提示您,文章內(nèi)容有點(diǎn)長(zhǎng),涉及的知識(shí)點(diǎn)比較多,所以,我推薦:”先頂后看“ ,先頂后看是21世紀(jì)看長(zhǎng)篇的***之道,是良好溝通的開(kāi)端,想知道是什么會(huì)讓你與眾不同嗎?想知道為什么上海今天會(huì)下這么大的雨么?請(qǐng)記住先頂后看,你頂?shù)牟皇俏业奈恼拢俏覀兠爸笥赀€要去上班的可貴精神!先頂后看,你值得擁有!

async/await如何提升IIS處理能力

首先響應(yīng)能力并不完全是說(shuō)我們程序性能的問(wèn)題,有時(shí)候可能你的程序沒(méi)有任何問(wèn)題,而且精心經(jīng)過(guò)優(yōu)化,可是響應(yīng)能力還是沒(méi)有上去,網(wǎng)站性能分析是一個(gè)復(fù)雜的活,有時(shí)候只能靠經(jīng)驗(yàn)和不斷的嘗試才能達(dá)到比較好的效果。當(dāng)然我們今天討論的主要是IIS的處理能力,或者也可能說(shuō)是IIS的性能,但絕非代碼本身的性能。即使async/await能夠提高IIS的處理能力,但是對(duì)于用戶來(lái)說(shuō)整個(gè)頁(yè)面從發(fā)起請(qǐng)求到頁(yè)面渲染完成的這些時(shí)間,是不會(huì)因?yàn)槲覀兗恿薬sync/await之后產(chǎn)生多大變化的。

另外異步的ASP.NET并非只有async/await才可以做的,ASP.NET在Web Form時(shí)代就已經(jīng)有異步Page了,包括ASP.NET MVC不是也有異步的Controller么?async/await 很新,很酷,但是它也只是在原有一技術(shù)基礎(chǔ)上做了一些改進(jìn),讓程序員們寫(xiě)起異步代碼來(lái)更容易了。大家常說(shuō)微軟喜歡新瓶裝舊酒,至少我們要看到這個(gè)新瓶給我們帶來(lái)了什么,不管是任何產(chǎn)品,都不可能一開(kāi)始就很***,所以不斷的迭代更新,也可以說(shuō)是一種正確做事的方式。

ASP.NET并行處理的步驟

 ASP.NET是如何在IIS中工作的一文已經(jīng)很詳細(xì)的介紹了一個(gè)請(qǐng)求是如何從客戶端到服務(wù)器的HTTP.SYS***進(jìn)入CLR進(jìn)行處理的(強(qiáng)烈建議不了解這一塊的同學(xué)先看這篇文章,有助于你理解本小節(jié)),但是所有的步驟都是基于一個(gè)線程的假設(shè)下進(jìn)行的。IIS本身就是一個(gè)多線程的工作環(huán)境,如果我們從多線程的視角來(lái)看會(huì)發(fā)生什么變化呢?我們首先來(lái)看一下下面這張圖。注意:我們下面的步驟是建立在IIS7.0以后的集成模式基礎(chǔ)之上的。

我們?cè)賮?lái)梳理一下上面的步驟:

  1. 所有的請(qǐng)求最開(kāi)始是由HTTP.SYS接收的,HTTP.SYS內(nèi)部有一個(gè)隊(duì)列維護(hù)著這些請(qǐng)求,這個(gè)隊(duì)列的request的數(shù)量大于一定數(shù)量(默認(rèn)是1000)的時(shí)候,HTTP.SYS就會(huì)直接返回503狀態(tài)(服務(wù)器忙),這是我們的***個(gè)閥門(mén)。
  2. HTTP.SYS把請(qǐng)求交給CLR 線程池中的IO線程
  3. CLR 線程池中的 Worker線程從IO線程中接過(guò)請(qǐng)求來(lái)處理,IO線程不必等待該請(qǐng)求的處理結(jié)果,而是可以返回繼續(xù)去處理HTTP.SYS隊(duì)列中的請(qǐng)求。IO線程和Worker線程的數(shù)量上限是第二個(gè)閥門(mén)。
  4. 當(dāng)CLR中正在被處理的請(qǐng)求數(shù)據(jù)大于一定值(***并行處理請(qǐng)求數(shù)量)的時(shí)候,從IO線程過(guò)來(lái)的請(qǐng)求就不會(huì)直接交給Worker線程,而是放到一個(gè)進(jìn)程池級(jí)別的一個(gè)隊(duì)列了,等到這個(gè)數(shù)量小于臨界值的時(shí)候,才會(huì)把它再次交給Worker線程去處理。這是我們的第三個(gè)閥門(mén)。

哪些因素會(huì)控制我們的響應(yīng)能力

從上面我們提到了幾大閥門(mén)中,我們可以得出下面的幾個(gè)數(shù)字控制或者說(shuō)影響著我們的響應(yīng)能力。

  1. HTTP.SYS隊(duì)列的長(zhǎng)度
  2. ***IO線程數(shù)量和***Worker線程數(shù)量
  3. ***并行處理請(qǐng)求數(shù)量

HTTP.SYS隊(duì)列的長(zhǎng)度

這個(gè)我覺(jué)得不需要額外解釋?zhuān)J(rèn)值是1000。這個(gè)值取決于我們我們后面CLR IO線程和Worker線程的處理速度,如果它們兩個(gè)都處理不了,這個(gè)數(shù)字再大也沒(méi)有用。因?yàn)?**他們會(huì)被存儲(chǔ)到進(jìn)程池級(jí)別的隊(duì)列中,所以只會(huì)造成內(nèi)存的浪費(fèi)。

***IO線程數(shù)量和***Worker線程數(shù)量

這兩個(gè)值是可以在web.config中進(jìn)行配置的。

maxIoThreads: 從HTTP.SYS隊(duì)列中拿請(qǐng)求的***IO線程數(shù)量

maxWorkerThreads: CLR中真實(shí)處理請(qǐng)求的***Worker線程數(shù)量

minIoThreads: 從HTTP.SYS隊(duì)列中拿請(qǐng)求的最小IO線程數(shù)量

minWorkerThreads:CLR中真實(shí)處理請(qǐng)求的最小Worker線程數(shù)量

minIoThreads和minWorkerThreads的默認(rèn)值是1,合理的加大他們可以避免不必要的線程創(chuàng)建和銷(xiāo)毀工作。maxIoThreads如果設(shè)置太大的話,或者說(shuō)不合理的話就會(huì)導(dǎo)致多數(shù)的request被放到進(jìn)程池級(jí)別的隊(duì)列中。所以 maxIoThreads和maxWorkerThreads是有一定關(guān)系的,假設(shè)1個(gè)worker線程1s可以處理10個(gè)請(qǐng)求,如果我們的機(jī)器配置只允許我們同時(shí)處理100個(gè)請(qǐng)求,那我們合理的maxThreads就是10。但是IO線程并不需要10s去處理一個(gè)請(qǐng)求,它比woker線程快,因?yàn)樗灰獜腍TTP.SYS的隊(duì)列那里拿過(guò)來(lái)就可以了,我們假設(shè)一個(gè)IO線程1s可以處理20個(gè)請(qǐng)求,對(duì)應(yīng)100個(gè)請(qǐng)求的上限,那我們maxIoThreads的合理值應(yīng)該是5。

***并行處理請(qǐng)求數(shù)量

進(jìn)程池級(jí)別的隊(duì)列給我們的CLR一定的緩沖,這里面要注意的是,這個(gè)隊(duì)列還沒(méi)有進(jìn)入到CLR,所以它不會(huì)占用我們托管環(huán)境的任何資源,也就是把請(qǐng)求卡在了CLR的外面。我們需要在aspnet.config級(jí)別進(jìn)行配置,我們可以在.net fraemwork的安裝目錄下找到它。一般是 C:\Windows\Microsoft.NET\Framework\v4.0.30319 如果你安裝的是4.0的話。

maxConcurrentRequestPerCPU: 每個(gè)CPU所允許的***并行處理請(qǐng)求數(shù)量,當(dāng)CLR中worker線程正在處理的請(qǐng)求之和大于這個(gè)數(shù)時(shí),從IO線程過(guò)來(lái)的請(qǐng)求就會(huì)被放到我們進(jìn)程池級(jí)別的隊(duì)列中。

maxConcurrentThreadsPerCPU: 設(shè)置為0即禁用。

requestQueueLimit:這個(gè)就是我們HTTP.SYS中隊(duì)列的那個(gè)長(zhǎng)度,我們可以在web.config/system.web/processModel結(jié)點(diǎn)下配置。

async和await 做了什么?

我們終于要切入正題了,拿ASP.NET MVC舉例,如果不采用async的Action,那么毫無(wú)疑問(wèn),它是在一個(gè)Woker線程中執(zhí)行的。當(dāng)我們?cè)L問(wèn)一些web service,或者讀文件的時(shí)候,這個(gè)Worker線程就會(huì)被阻塞。假設(shè)我們這個(gè)Action執(zhí)行時(shí)間一共是100ms,其它訪問(wèn)web service花了80ms,理想情況下一個(gè)Worker線程一秒可以響應(yīng)10個(gè)請(qǐng)求,假設(shè)我們的maxWorkerThreads是10,那我們一秒內(nèi)總是可響應(yīng)請(qǐng)求就是100。如果說(shuō)我們想把這個(gè)可響應(yīng)請(qǐng)求數(shù)升到200怎么做呢?

有人會(huì)說(shuō),這還不簡(jiǎn)單,把maxWorkerThreads調(diào)20不就行了么? 其實(shí)我們做也沒(méi)有什么 問(wèn)題,確實(shí)是可以的,而且也確實(shí)能起到作用。那我們?yōu)槭裁催€要大費(fèi)周章的搞什么 async/await呢?搞得腦子都暈了?async/await給我們解決了什么問(wèn)題?它可以在我們?cè)L問(wèn)web service的時(shí)候把當(dāng)前的worker線程放走,將它放回線程池,這樣它就可以去處理其它的請(qǐng)求了。和IO線程一樣,IO線程只負(fù)責(zé)把請(qǐng)求交給Worker線程或者放入進(jìn)程池級(jí)別的隊(duì)列,然后又去HTTP.SYS的隊(duì)列中處理其它的請(qǐng)求。等到web service給我們返回結(jié)果了,會(huì)再到線程池中隨機(jī)拿一個(gè)新的woker線程繼續(xù)往下執(zhí)行。也就是說(shuō)我們減少了那一部分等待的時(shí)間,充份利用了線程。

我們來(lái)對(duì)比一下使用async/awit和不使用的情況,

不使用async/await: 20個(gè)woker線程1s可以處理200個(gè)請(qǐng)求。

那轉(zhuǎn)換成總的時(shí)間的就是 20 * 1000ms =  20000ms,

其中等待的時(shí)間為 200 * 80ms = 16000ms。

也就是說(shuō)使用async/await我們至少節(jié)約了16000ms的時(shí)間,這20個(gè)worker線程又會(huì)再去處理請(qǐng)求,即使按照每個(gè)請(qǐng)求100ms的處理時(shí)間我們還可以再增加160個(gè)請(qǐng)求。而且別忘了100ms是基于同步情況下,包括等待時(shí)間在內(nèi)的基礎(chǔ)上得到的,所以實(shí)際情況可能還要多,當(dāng)然我們這里沒(méi)有算上線程切換的時(shí)間,所以實(shí)際情況中是有一點(diǎn)差異的,但是應(yīng)該不會(huì)很大,因?yàn)槲覀兊木€程都是基于線程池的操作。

所有結(jié)果是20個(gè)Worker線程不使用異步的情況下,1s能自理200個(gè)請(qǐng)求,而使用異步的情況下可以處理360個(gè)請(qǐng)求,立馬提升80%呀!采用異步之后,對(duì)于同樣的請(qǐng)求數(shù)量,需要的Worker線程數(shù)據(jù)會(huì)大大減少50%左右,一個(gè)線程至少會(huì)在堆上分配1M的內(nèi)存,如果是1000個(gè)線程那就是1G的容量,雖然內(nèi)存現(xiàn)在便宜,但是省著總結(jié)是好的嘛,而且更少的線程是可以減少線程池在維護(hù)線程時(shí)產(chǎn)生的CPU消耗的。

注意:以上數(shù)據(jù)并非真實(shí)測(cè)試數(shù)據(jù),真實(shí)情況一個(gè)request的時(shí)間也并非100ms,花費(fèi)在web service上的時(shí)間也并非80ms,僅僅是給大家一個(gè)思路:),所以這里面用了async和await之后對(duì)響應(yīng)能力有多大的提升和我們?cè)瓉?lái)堵塞在這些IO和網(wǎng)絡(luò)上的時(shí)間是有很大的關(guān)系的。

幾點(diǎn)建議

看到這里,不知道大家有沒(méi)有得到點(diǎn)什么。首先***點(diǎn)我們要知道的是async/await不是***藥,不們不能指望光寫(xiě)兩個(gè)光鍵字就希望性能的提升。要記住,一個(gè)CPU在同一時(shí)間段內(nèi)是只能執(zhí)行一個(gè)線程的。所以這也是為什么async和await建議在IO或者是網(wǎng)絡(luò)操作的時(shí)候使用。我們的MVC站點(diǎn)訪問(wèn)WCF或者Web Service這種場(chǎng)景就非常的適合使用異步來(lái)操作。在上面的例子中80ms讀取web service的時(shí)間,大部份時(shí)間都是不需要cpu操作的,這樣cpu才可以被其它的線程利用,如果不是一個(gè)讀取web service的操作,而是一個(gè)復(fù)雜計(jì)算的操作,那你就等著cpu爆表吧。

第二點(diǎn)是,除了程序中利用異步,我們上面講到的關(guān)于IIS的配置是很重要的,如果使用了異步,請(qǐng)記得把maxWorkerThreads和maxConcurrentRequestPerCPU的值調(diào)高試試。

#p#

早期對(duì)Web service的異步編程模式APM

講完我們高大上的async/await之后,我們來(lái)看看這個(gè)技術(shù)很老,但是概念確依舊延續(xù)至今的Web Service。 我們這里所說(shuō)的針對(duì)web service的異步編程模式不是指在服務(wù)器端的web service本身,而是指調(diào)用web service的客戶端。大家知道對(duì)于web service,我們通過(guò)添加web service引用或者.net提供的生成工具就可以生成相應(yīng)的代理類(lèi),可以讓我們像調(diào)用本地代碼一樣訪問(wèn)web service,而所生成的代碼類(lèi)中對(duì)針對(duì)每一個(gè)web service方法生成3個(gè)對(duì)應(yīng)的方法,比如說(shuō)我們的方法名叫DownloadContent,除了這個(gè)方法之外還有BeginDownloadContent和EndDownloadContent方法,而這兩個(gè)就是我們今天要說(shuō)的早期的異步編程模式APM(Asynchronous Programming Model)。下面就來(lái)看看我們web service中的代碼,注意我們現(xiàn)在的項(xiàng)目都是在.NET Framework3.5下實(shí)現(xiàn)的。

PageContent.asmx的代碼

  1. public class PageContent : System.Web.Services.WebService  
  2. {  
  3.     [WebMethod]  
  4.     public string DownloadContent(string url)  
  5.     {  
  6.         var client = new System.Net.WebClient();  
  7.         return client.DownloadString(url);  
  8.     }  

注意我們web service中的DownloadContent方法調(diào)用的是WebClient的同步方法,WebClient也有異步方法即:DownloadStringAsync。但是大家要明白,不管服務(wù)器是同步還是異步,對(duì)于客戶端來(lái)說(shuō)調(diào)用了你這個(gè)web service都是一樣的,就是得等你返回結(jié)果。

當(dāng)然,我們也可以像MVC里面的代碼一樣,把我們的服務(wù)器端也寫(xiě)成異步的。那得到好處的是那個(gè)托管web service的服務(wù)器,它的處理能力得到提高,就像ASP.NET一樣。如果我們用JavaScript去調(diào)用這個(gè)Web Service,那么Ajax(Asynchronous Javascript + XML)就是我們客戶端用到的異步編程技術(shù)。如果是其它的客戶端呢?比如說(shuō)一個(gè)CS的桌面程序?我們需要異步編程么?

當(dāng)WinForm遇上Web Service

WinForm不像托管在IIS的ASP.NET網(wǎng)站,會(huì)有一個(gè)線程池管理著多個(gè)線程來(lái)處理用戶的請(qǐng)求,換個(gè)說(shuō)法ASP.NET網(wǎng)站生來(lái)就是基于多線程的。但是,在WinForm中,如果我們不刻意使用多線程,那至始至終,都只有一個(gè)線程,稱之為UI線程。也許在一些小型的系統(tǒng)中WinForm很少涉及到多線程,因?yàn)閃inForm本身的優(yōu)勢(shì)就在它是獨(dú)立運(yùn)行在客戶端的,在性能上和可操作性上都會(huì)有很大的優(yōu)勢(shì)。所以很多中小型的WinForm系統(tǒng)都是直接就訪問(wèn)數(shù)據(jù)庫(kù)了,并且基本上也只有數(shù)據(jù)的傳輸,什么圖片資源那是很少的,所以等待的時(shí)間是很短的,基本不用費(fèi)什么腦力去考慮什么3秒之內(nèi)必須將頁(yè)面顯示到用戶面前這種問(wèn)題。

既然WinForm在性能上有這么大的優(yōu)勢(shì),那它還需要異步嗎?

我們上面說(shuō)的是中小型的WinForm,如果是大型的系統(tǒng)呢?如果WinForm只是其它的很小一部分,就像我們文章開(kāi)始說(shuō)的還有很多其它成千上萬(wàn)個(gè)手機(jī)客戶端,Web客戶端,平板客戶端呢?如果客戶端很多導(dǎo)致數(shù)據(jù)庫(kù)撐不住怎么辦? 想在中間加一層緩存怎么辦?

拿一個(gè)b2b的網(wǎng)站功能舉例,用戶可以通過(guò)網(wǎng)站下單,手機(jī)也可以下單,還可以通過(guò)電腦的桌面客戶端下單。在下完單之后要完成交易,庫(kù)存扣減,發(fā)送訂單確認(rèn)通知等等功能,而不管你的訂單是通過(guò)哪個(gè)端完成的,這些功能我們都要去做,對(duì)嗎?那我們就不能單獨(dú)放在WinForm里面了,不然這些代碼在其它的端里面又得全部全新再一一實(shí)現(xiàn),同樣的代碼放在不同的地方那可是相當(dāng)危險(xiǎn)的,所以就有了我們后來(lái)的SOA架構(gòu),把這些功能都抽成服務(wù),每種類(lèi)型的端都是調(diào)用服務(wù)就可以了。一是可以統(tǒng)一維護(hù)這些功能,二是可以很方便的做擴(kuò)展,去更好的適應(yīng)功能和架構(gòu)上的擴(kuò)展。比如說(shuō)像下面這樣的一個(gè)系統(tǒng)。

 

在上圖中,Web端雖然也是屬于我們平常說(shuō)的服務(wù)端(甚至是由多臺(tái)服務(wù)器組成的web群集),但是對(duì)我們整個(gè)系統(tǒng)來(lái)說(shuō),它也只是一個(gè)端而已。對(duì)于一個(gè)端來(lái)說(shuō),它本身只處理和用戶交互的問(wèn)題,其余所有的功能,業(yè)務(wù)都會(huì)交給后來(lái)臺(tái)處理。在我們上面的架構(gòu)中,應(yīng)用層都不會(huì)直接參加真正業(yè)務(wù)邏輯相關(guān)的處理,而是放到我們更下層數(shù)據(jù)層去做處理。那么應(yīng)用層主要協(xié)助做一些與用戶交互的一些功能,如果手機(jī)短信發(fā)送,郵件發(fā)送等等,并且可以根據(jù)優(yōu)先級(jí)選擇是放入隊(duì)列中稍候處理還是直接調(diào)用功能服務(wù)立即處理。

在這樣的一個(gè)系統(tǒng)中,我們的Web服務(wù)器也好,Winform端也好都將只是整個(gè)系統(tǒng)中的一個(gè)終端,它們主要的任何是用戶和后面服務(wù)之間的一個(gè)橋梁。涉及到Service的調(diào)用之后,為了給用戶良好的用戶體驗(yàn),在WinForm端,我們自然就要考慮異步的問(wèn)題。 

WinForm異步調(diào)用Web Service

有了像VS這樣強(qiáng)大的工具為我們生成代理類(lèi),我們?cè)趯?xiě)調(diào)用Web service的代碼時(shí)就可以像調(diào)用本地類(lèi)庫(kù)一樣調(diào)用Web Service了,我們只需要添加一個(gè)Web Reference就可以了。

// Form1.cs的代碼

  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     var pageContentService = new localhost.PageContent();  
  4.     pageContentService.BeginDownloadContent(  
  5.         "http://jesse2013.cnblogs.com",  
  6.         new AsyncCallback(DownloadContentCallback),  
  7.         pageContentService);  
  8. }  
  9.    
  10. private void DownloadContentCallback(IAsyncResult result)  
  11. {  
  12.     var pageContentService = (localhost.PageContent)result.AsyncState;  
  13.     var msg = pageContentService.EndDownloadContent(result);  
  14.     MessageBox.Show(msg);  

代碼非常的簡(jiǎn)單,在執(zhí)行完pageContentService.BeginDownloadContent之后,我們的主線程就返回了。在調(diào)用Web service這段時(shí)間內(nèi)我們的UI不會(huì)被阻塞,也不會(huì)出現(xiàn)“無(wú)法響應(yīng)這種情況”,我們依然可以拖動(dòng)窗體甚至做其它的事情。這就是APM的魔力,但是我們的callback究竟是在哪個(gè)線程中執(zhí)行的呢?是線程池中的線程么?咋們接著往下看。

#p#

APM異步編程模式詳解

線程問(wèn)題

接下來(lái)我們就是更進(jìn)一步的了解APM這種模式是如何工作的,但是首先我們要回答上面留下來(lái)的問(wèn)題,這種異步的編程方式有沒(méi)有為我們開(kāi)啟新的線程?讓代碼說(shuō)話:

  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     Trace.TraceInformation("Is current thread from thread pool? {0}", Thread.CurrentThread.IsThreadPoolThread ? "Yes" : "No");  
  4.     Trace.TraceInformation("Start calling web service on thread: {0}", Thread.CurrentThread.ManagedThreadId);  
  5.     var pageContentService = new localhost.PageContent();  
  6.     pageContentService.BeginDownloadContent(  
  7.         "http://jesse2013.cnblogs.com",  
  8.         new AsyncCallback(DownloadContentCallback),  
  9.         pageContentService);  
  10. }  
  11.    
  12. private void DownloadContentCallback(IAsyncResult result)  
  13. {  
  14.     var pageContentService = (localhost.PageContent)result.AsyncState;  
  15.     var msg = pageContentService.EndDownloadContent(result);  
  16.    
  17.     Trace.TraceInformation("Is current thread from thread pool? {0}" , Thread.CurrentThread.IsThreadPoolThread ? "Yes" : "No");  
  18.     Trace.TraceInformation("End calling web service on thread: {0}, the result of the web service is: {1}",  
  19.         Thread.CurrentThread.ManagedThreadId,  
  20.         msg);  

我們?cè)诎粹o點(diǎn)擊的方法和callback方法中分別輸出當(dāng)前線程的ID,以及他們是否屬于線程池的線程,得到的結(jié)果如下:

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

按鈕點(diǎn)擊的方法是由UI直接控制,很明顯它不是一個(gè)線程池線程,也不是后臺(tái)線程。而我們的callback卻是在一個(gè)來(lái)自于線程池的后臺(tái)線程執(zhí)行的,答案揭曉了,可是這會(huì)給我們帶來(lái)一個(gè)問(wèn)題,我們上面講了只有UI線程也可以去更新我們的UI控件,也就是說(shuō)在callback中我們是不能更新UI控件的,那我們?nèi)绾巫尭耈I讓用戶知道反饋呢?答案在后面接曉 :),讓我們先專(zhuān)注于把APM弄清楚。

從Delegate開(kāi)始

其實(shí),APM在.NET3.5以前都被廣泛使用,在WinForm窗體控制中,在一個(gè)IO操作的類(lèi)庫(kù)中等等!大家可以很容易的找到搭配了Begin和End的方法,更重要的是只要是有代理的地方,我們都可以使用APM這種模式。我們來(lái)看一個(gè)很簡(jiǎn)單的例子:

  1. delegate void EatAsync(string food);  
  2. private void button2_Click(object sender, EventArgs e)  
  3. {  
  4.     var myAsync = new EatAsync(eat);  
  5.     Trace.TraceInformation("Activate eating on thread: {0}", Thread.CurrentThread.ManagedThreadId);  
  6.     myAsync.BeginInvoke("icecream"new AsyncCallback(clean), myAsync);  
  7. }  
  8.    
  9. private void eat(string food)  
  10. {  
  11.     Trace.TraceInformation("I am eating.... on thread: {0}", Thread.CurrentThread.ManagedThreadId);  
  12. }  
  13.    
  14. private void clean(IAsyncResult asyncResult)  
  15. {  
  16.     Trace.TraceInformation("I am done eating.... on thread: {0}", Thread.CurrentThread.ManagedThreadId);  

上面的代碼中,我們通過(guò)把eat封裝成一個(gè)委托,然后再調(diào)用該委托的BeginInvoke方法實(shí)現(xiàn)了異步的執(zhí)行。也就是實(shí)際的eat方法不是在主線程中執(zhí)行的,我們可以看輸出的結(jié)果:

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

clean是我們傳進(jìn)去的callback,該方法會(huì)在我們的eat方法執(zhí)行完之后被調(diào)用,所以它會(huì)和我們eat方法在同一個(gè)線程中被調(diào)用。大家如果熟悉代理的話就會(huì)知道,代碼實(shí)際上會(huì)被編譯成一個(gè)類(lèi),而B(niǎo)eginInvoke和EndInvoke方法正是編譯器為我們自動(dòng)加進(jìn)去的方法,我們不用額外做任何事情,這在早期沒(méi)有TPL和async/await之前(APM從.NET1.0時(shí)代就有了),的確是一個(gè)不錯(cuò)的選擇。

再次認(rèn)識(shí)APM

了解了Delegate實(shí)現(xiàn)的BeginInvoke和EndInvoke之后,我們?cè)賮?lái)分析一下APM用到的那些對(duì)象。 拿我們Web service的代理類(lèi)來(lái)舉例,它為我們生成了以下3個(gè)方法:

  1. string DownloadContent(string url): 同步方法
  2. IAsyncResult BeginDownloadContent(string url, AsyncCallback callback, object asyncState): 異步開(kāi)始方法
  3. EndDownloadContent(IAsyncResult asyncResult):異步結(jié)束方法

在我們調(diào)用EndDownloadContent方法的時(shí)候,如果我們的web service調(diào)用還沒(méi)有返回,那這個(gè)時(shí)候就會(huì)用阻塞的方式去拿結(jié)果。但是在我們傳到BeginDownloadContent中的callback被調(diào)用的時(shí)候,那操作一定是已經(jīng)完成了,也就是說(shuō)IAsyncResult.IsCompleted = true。而在APM異步編程模式中Begin方法總是返回IAsyncResult這個(gè)接口的實(shí)現(xiàn)。IAsyncReuslt僅僅包含以下4個(gè)屬性:

WaitHanlde通常作為同步對(duì)象的基類(lèi),并且可以利用它來(lái)阻塞線程,更多信息可以參考MSDN 。 借助于IAsyncResult的幫助,我們就可以通過(guò)以下幾種方式去獲取當(dāng)同所執(zhí)行操作的結(jié)果。

  1. 輪詢
  2. 強(qiáng)制等待
  3. 完成通知

完成通知就是們上面用到的那種,調(diào)完Begin方法之后,主線程就算完成任務(wù)了。我們也不用監(jiān)控該操作的執(zhí)行情況,當(dāng)該操作執(zhí)行完之后,我們?cè)贐egin方法中傳進(jìn)去的callback就會(huì)被調(diào)用了,我們可以在那個(gè)方法中調(diào)用End方法去獲取結(jié)果。下面我們?cè)俸?jiǎn)單說(shuō)一下前面兩種方式。

//輪詢獲取結(jié)果代碼

  1. var pageContentService = new localhost.PageContent();  
  2. IAsyncResult asyncResult = pageContentService.BeginDownloadContent(  
  3.     "http://jesse2013.cnblogs.com",  
  4.     null,  
  5.     pageContentService);  
  6.    
  7. while (!asyncResult.IsCompleted)  
  8. {  
  9.     Thread.Sleep(100);  
  10. }  
  11. var content = pageContentService.EndDownloadContent(asyncResult); 

 // 強(qiáng)制等待結(jié)果代碼

  1. var pageContentService = new localhost.PageContent();  
  2. IAsyncResult asyncResult = pageContentService.BeginDownloadContent(  
  3.     "http://jesse2013.cnblogs.com",  
  4.     null,  
  5.     pageContentService);  
  6.    
  7. // 也可以調(diào)用WaitOne()的無(wú)參版本,不限制強(qiáng)制等待時(shí)間  
  8. if (asyncResult.AsyncWaitHandle.WaitOne(2000))  
  9. {  
  10.     var content = pageContentService.EndDownloadContent(asyncResult);  
  11. }  
  12. else 
  13. {   
  14.     // 2s時(shí)間已經(jīng)過(guò)了,但是還沒(méi)有執(zhí)行完     

#p#

EAP(Event-Based Asynchronous Pattern)

EAP是在.NET2.0推出的另一種過(guò)渡的異步編程模型,也是在.NET3.5以后Microsoft支持的一種做法,為什么呢? 如果大家建一個(gè).NET4.0或者更高版本的WinForm項(xiàng)目,再去添加Web Reference就會(huì)發(fā)現(xiàn)生成的代理類(lèi)中已經(jīng)沒(méi)有Begin和End方法了,記住在3.5的時(shí)候是兩者共存的,你可以選擇任意一種來(lái)使用。但是到了.NET4.0以后,EAP成為了你唯一的選擇。(我沒(méi)有嘗試過(guò)手動(dòng)生成代理類(lèi),有興趣的同學(xué)可以嘗試一下)讓我們來(lái)看一下在.NET4下,我們是如何異步調(diào)用Web Service的。

  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     var pageContent = new localhost.PageContent();  
  4.     pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");  
  5.     pageContent.DownloadContentCompleted += pageContent_DownloadContentCompleted;  
  6. }  
  7.    
  8. private void pageContent_DownloadContentCompleted(object sender, localhost.DownloadContentCompletedEventArgs e)  
  9. {  
  10.     if (e.Error == null)  
  11.     {  
  12.         textBox1.Text = e.Result;  
  13.     }  
  14.     else 
  15.     {   
  16.         // 出錯(cuò)了  
  17.     }  

線程問(wèn)題

不知道大家還是否記得,在APM模式中,callback是執(zhí)行在另一個(gè)線程中,不能隨易的去更新UI。但是如果你仔細(xì)看一下上面的代碼,我們的DownloadContentCompleted事件綁定的方法中直接就更新了UI,把返回的內(nèi)容寫(xiě)到了一個(gè)文本框里面。通過(guò)同樣的方法可以發(fā)現(xiàn),在EAP這種異步編程模式下,事件綁定的方法也是在調(diào)用的那個(gè)線程中執(zhí)行的。也就是說(shuō)解決了異步編程的時(shí)候UI交互的問(wèn)題,而且是在同一個(gè)線程中執(zhí)行。 看看下面的代碼:

  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     Trace.TraceInformation("Call DownloadContentAsync on thread: {0}", Thread.CurrentThread.ManagedThreadId);  
  4.     Trace.TraceInformation("Is current from thread pool? : {0}", Thread.CurrentThread.IsThreadPoolThread ? "YES" : "NO");  
  5.    
  6.     var pageContent = new localhost.PageContent();  
  7.     pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");  
  8.     pageContent.DownloadContentCompleted += pageContent_DownloadContentCompleted;  
  9. }  
  10.    
  11. private void pageContent_DownloadContentCompleted(object sender, localhost.DownloadContentCompletedEventArgs e)  
  12. {  
  13.     Trace.TraceInformation("Completed DownloadContentAsync on thread: {0}", Thread.CurrentThread.ManagedThreadId);  
  14.     Trace.TraceInformation("Is current from thread pool? : {0}", Thread.CurrentThread.IsThreadPoolThread ? "YES" : "NO");  
  1. Desktop4.vshost.exe Information: 0 : Call DownloadContentAsync on thread: 10  
  2. Desktop4.vshost.exe Information: 0 : Is current from thread pool? : NO  
  3. Desktop4.vshost.exe Information: 0 : Completed DownloadContentAsync on thread: 10  
  4. Desktop4.vshost.exe Information: 0 : Is current from thread pool? : NO 

#p#

async/await 給WinFrom帶來(lái)了什么

如果說(shuō)async給ASP.NET帶來(lái)的是處理能力的提高,那么在WinForm中給程序員帶來(lái)的好處則是***的。我們?cè)僖膊挥靡驗(yàn)橐獙?shí)現(xiàn)異步寫(xiě)回調(diào)或者綁定事件了,省事了,可讀性也提高了。不信你看下面我們將調(diào)用我們那個(gè)web service的代碼在.NET4.5下實(shí)現(xiàn)一下:

  1. private async void button2_Click(object sender, EventArgs e)  
  2. {  
  3.     var pageContent = new localhost.PageContentSoapClient();  
  4.     var content = await pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com");  
  5.    
  6.     textBox1.Text = content.Body.DownloadContentResult;  

簡(jiǎn)單的三行代碼,像寫(xiě)同步代碼一樣寫(xiě)異步代碼,我想也許這就是async/await的魔力吧。在await之后,UI線程就可以回去響應(yīng)UI了,在上面的代碼中我們是沒(méi)有新線程產(chǎn)生的,和EAP一樣拿到結(jié)果直接就可以對(duì)UI操作了。

async/await似乎真的很好,但是如果我們await后面的代碼執(zhí)行在另外一個(gè)線程中會(huì)發(fā)生什么事情呢?

  1. private async void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     label1.Text = "Calculating Sqrt of 5000000";  
  4.     button1.Enabled = false;  
  5.     progressBar1.Visible = true;  
  6.    
  7.     double sqrt = await Task<double>.Run(() =>  
  8.     {  
  9.         double result = 0;  
  10.         for (int i = 0; i < 50000000; i++)  
  11.         {  
  12.             result += Math.Sqrt(i);  
  13.    
  14.             progressBar1.Maximum = 50000000;  
  15.             progressBar1.Value = i;  
  16.         }  
  17.         return result;  
  18.     });  
  19.    
  20.     progressBar1.Visible = false;  
  21.     button1.Enabled = true;  
  22.     label1.Text = "The sqrt of 50000000 is " + sqrt;  

我們?cè)诮缑嬷蟹帕艘粋€(gè)ProgressBar,同時(shí)開(kāi)一個(gè)線程去把從1到5000000的平方全部加起來(lái),看起來(lái)是一個(gè)非常耗時(shí)的操作,于是我們用Task.Run開(kāi)了一個(gè)新的線程去執(zhí)行。(注:如果是純運(yùn)算的操作,多線程操作對(duì)性能沒(méi)有多大幫助,我們這里主要是想給UI一個(gè)進(jìn)度顯示當(dāng)前進(jìn)行到哪一步了。)看起來(lái)沒(méi)有什么問(wèn)題,我們按F5運(yùn)行吧!
Bomb~

當(dāng)執(zhí)行到這里的時(shí)候,程序就崩潰了,告訴我們”無(wú)效操作,只能從創(chuàng)建porgressBar的線程訪問(wèn)它。“  這也是我們一開(kāi)始提到的,在WinForm程序中,只有UI主線程才能對(duì)UI進(jìn)行操作,其它的線程是沒(méi)有權(quán)限的。接下來(lái)我們就來(lái)看看,如果在WinForm中實(shí)現(xiàn)非UI線程對(duì)UI控制的更新操作。 

#p#

不同線程之間通訊的問(wèn)題

***的Invoke

WinForm中絕大多數(shù)的控件包括窗體在內(nèi)都實(shí)現(xiàn)了Invoke方法,可以傳入一個(gè)Delegate,這個(gè)Delegate將會(huì)被擁有那個(gè)控制的線程所調(diào)用,從而避免了跨線程訪問(wèn)的問(wèn)題。

  1. Trace.TraceInformation("UI Thread : {0}", Thread.CurrentThread.ManagedThreadId);  
  2. double sqrt = await Task<double>.Run(() =>  
  3. {  
  4.     Trace.TraceInformation("Run calculation on thread: {0}", Thread.CurrentThread.ManagedThreadId);  
  5.     double result = 0;  
  6.     for (int i = 0; i < 50000000; i++)  
  7.     {  
  8.         result += Math.Sqrt(i);  
  9.         progressBar1.Invoke(new Action(() => {  
  10.             Trace.TraceInformation("Update UI on thread: {0}", Thread.CurrentThread.ManagedThreadId);  
  11.             progressBar1.Maximum = 50000000;  
  12.             progressBar1.Value = i;  
  13.         }));  
  14.     }  
  15.     return result;  
  16. }); 
  1. Desktop.vshost.exe Information: 0 : UI Thread : 9  
  2. Desktop.vshost.exe Information: 0 : Run calculation on thread: 10  
  3. Desktop.vshost.exe Information: 0 : Update UI on thread: 9 

Invoke方法比較簡(jiǎn)單,我們就不做過(guò)多的研究了,但是我們要考慮到一點(diǎn),Invoke是WinForm實(shí)現(xiàn)的UI同交互技術(shù),WPF用的卻是Dispatcher,如果是在ASP.NET下跨線程之間的同步又怎么辦呢。為了兼容各種技術(shù)平臺(tái)下,跨線程同步的問(wèn)題,Microsoft在.NET2.0的時(shí)候就引入了我們下面的這個(gè)對(duì)象。

SynchronizationContext上下文同步對(duì)象

為什么需要SynchronizationContext

就像我們?cè)赪inForm中遇到的問(wèn)題一樣,有時(shí)候我們需要在一個(gè)線程中傳遞一些數(shù)據(jù)或者做一些操作到另一個(gè)線程。但是在絕大多數(shù)情況下這是不允許的,出于安全因素的考慮,每一個(gè)線程都有它獨(dú)立的內(nèi)存空間和上下文。因此在.NET2.0,微軟推出了SynchronizationContext。

它主要的功能之一是為我們提供了一種將一些工作任務(wù)(Delegate)以隊(duì)列的方式存儲(chǔ)在一個(gè)上下文對(duì)象中,然后把這些上下文對(duì)象關(guān)聯(lián)到具體的線程上,當(dāng)然有時(shí)候多個(gè)線程也可以關(guān)聯(lián)到同一個(gè)SynchronizationContext對(duì)象。獲取當(dāng)前線程的同步上下文對(duì)象可以使用SynchronizationContext.Current。同時(shí)它還為我們提供以下兩個(gè)方法Post和Send,分別是以異步和同步的方法將我們上面說(shuō)的工作任務(wù)放到我們SynchronizationContext的隊(duì)列中。

SynchronizationContext示例

還是拿我們上面Invoke中用到的例子舉例,只是這次我們不直接調(diào)用控件的Invoke方法去更新它,而是寫(xiě)了一個(gè)Report的方法專(zhuān)門(mén)去更新UI。

  1. double sqrt = await Task<double>.Run(() =>  
  2. {  
  3.     Trace.TraceInformation("Current thread id is:{0}", Thread.CurrentThread.ManagedThreadId);  
  4.    
  5.     double result = 0;  
  6.     for (int i = 0; i < 50000000; i++)  
  7.     {  
  8.         result += Math.Sqrt(i);  
  9.         Report(new Tuple<intint>(50000000, i));  
  10.     }  
  11.     return result;  
  12. }); 

每一次操作完之后我們調(diào)用一下Report方法,把我們總共要算的數(shù)字,以前當(dāng)前正在計(jì)算的數(shù)字傳給它就可以了。接下來(lái)就看我們的Report方法了。

  1. private SynchronizationContext m_SynchronizationContext;  
  2. private DateTime m_PreviousTime = DateTime.Now;  
  3.    
  4. public Form1()  
  5. {  
  6.     InitializeComponent();  
  7.     // 在全局保存當(dāng)前UI線程的SynchronizationContext對(duì)象  
  8.     m_SynchronizationContext = SynchronizationContext.Current;  
  9. }  
  10.    
  11. public void Report(Tuple<intint> value)  
  12. {  
  13.     DateTime now = DateTime.Now;  
  14.     if ((now - m_PreviousTime).Milliseconds > 100)  
  15.     {  
  16.         m_SynchronizationContext.Post((obj) =>  
  17.         {  
  18.             Tuple<intint> minMax = (Tuple<intint>)obj;  
  19.             progressBar1.Maximum = minMax.Item1;  
  20.             progressBar1.Value = minMax.Item2;  
  21.         }, value);  
  22.    
  23.         m_PreviousTime = now;  
  24.     }  

整個(gè)操作看起來(lái)要比Inovke復(fù)雜一點(diǎn),與Invoke不同的是SynchronizationContext不需要對(duì)Control的引用,而Invoke必須先得有那個(gè)控件才能調(diào)用它的Invoke方法去更新它。

#p#

小結(jié)

這篇博客內(nèi)容有點(diǎn)長(zhǎng),不知道有多少人可以看到這里:)。最開(kāi)始我只是想寫(xiě)寫(xiě)WinFrom下異步調(diào)用Web Service的一些東西,在我開(kāi)始這篇文件的題目是”異步編程在WinForm下的實(shí)踐“,但是寫(xiě)著寫(xiě)著發(fā)現(xiàn)越來(lái)越多的迷團(tuán)沒(méi)有解開(kāi),其實(shí)都是一些老的技術(shù)以前沒(méi)有接觸和掌握好,所以所幸就一次性把他們都重新學(xué)習(xí)了一遍,與大家分享。

我們?cè)賮?lái)回顧一下文章所涉及到的一些重要的概念:

  1. async/await 在ASP.NET做的***貢獻(xiàn)(早期ASP.NET的異步開(kāi)發(fā)模式同樣也有這樣的貢獻(xiàn)),是在訪問(wèn)數(shù)據(jù)庫(kù)的時(shí)候、訪問(wèn)遠(yuǎn)程IO的時(shí)候及時(shí)釋放了當(dāng)前的處理性程,可以讓這些線程回到線程池中,從而實(shí)現(xiàn)可以去處理其它請(qǐng)求的功能。
  2. 異步的ASP.NET開(kāi)發(fā)能夠在處理能力上帶來(lái)多大的提高,取決于我們的程序有多少時(shí)間是被阻塞的,也就是那些訪問(wèn)數(shù)據(jù)庫(kù)和遠(yuǎn)程Service的時(shí)間。
  3. 除了將代碼改成異步,我們還需要在IIS上做一些相對(duì)的配置來(lái)實(shí)現(xiàn)***化。
  4. 不管是ASP.NET、WinForm還是Mobile、還是平板,在大型系統(tǒng)中都只是一個(gè)與用戶交互的端而已,所以不管你現(xiàn)在是做所謂的前端(JavaScript + CSS等),還是所謂的后端(ASP.NET MVC、WCF、Web API 等 ),又或者是比較時(shí)髦的移動(dòng)端(IOS也好,Andrioid也罷,哪怕是不爭(zhēng)氣的WP),都只是整個(gè)大型系統(tǒng)中的零星一角而已。當(dāng)然我并不是貶低這些端的價(jià)值,正是因?yàn)槲覀儗?zhuān)注于不同,努力提高每一個(gè)端的用戶體驗(yàn),才能讓這些大型系統(tǒng)有露臉的機(jī)會(huì)。我想說(shuō)的是,在你對(duì)現(xiàn)在技術(shù)取得一定的成就之后,不要停止學(xué)習(xí),因?yàn)檎麄€(gè)軟件架構(gòu)體系中還有很多很多美妙的東西值得我們?nèi)グl(fā)現(xiàn)。
  5. APM和EAP是在async/await之前的兩種不同的異步編程模式。
  6. APM如果不阻塞主線程,那么完成通知(回調(diào))就會(huì)執(zhí)行在另外一個(gè)線程中,從而給我們更新UI帶來(lái)一定的問(wèn)題。
  7. EAP的通知事件是在主線程中執(zhí)行的,不會(huì)存在UI交互的問(wèn)題。
  8. ***,我們還學(xué)習(xí)了在Winform下不同線程之間交互的問(wèn)題,以及SynchronizationContext。
  9. APM是.NET下最早的異步編程方法,從.NET1.0以來(lái)就有了。在.NET2.0的時(shí)候,微軟意識(shí)到了APM的回調(diào)函數(shù)中與UI交互的問(wèn)題,于是帶來(lái)了新的EAP。APM與EAP一直共存到.NET3.5,在.NET4.0的時(shí)候微軟帶來(lái)了TPL,也就是我們所熟知的Task編程,而.NET4.5就是我們大家知道的async/await了,可以看到.NET一直在不停的進(jìn)步,加上最近不斷的和開(kāi)源社區(qū)的合作,跨平臺(tái)等特性的引入,我們有理由相信.NET會(huì)越走越好。

***,這篇文章從找資料學(xué)習(xí)到寫(xiě)出來(lái),差不多花了我兩個(gè)周未的時(shí)間,希望能夠給需要的人或者感興趣想要不斷學(xué)習(xí)的人一點(diǎn)幫助(不管是往前學(xué)習(xí),還是往后學(xué)習(xí))。 

引用 & 擴(kuò)展閱讀

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

本文來(lái)自:http://www.cnblogs.com/jesse2013/p/Asynchronous-Programming-In-DotNet.html#aspnet-async

責(zé)任編輯:林師授 來(lái)源: Jesse Liu的博客
相關(guān)推薦

2013-04-01 15:38:54

異步編程異步編程模型

2021-06-28 08:10:59

JavaScript異步編程

2024-06-25 08:33:48

2013-05-16 10:33:11

C#C# 5.0Async

2024-12-23 08:00:45

2021-02-09 09:53:11

C#多線程異步

2017-08-02 14:17:08

前端asyncawait

2024-04-01 09:45:50

TAP模式.NET異步編程

2023-10-08 10:21:11

JavaScriptAsync

2024-10-07 08:28:03

WPFUI應(yīng)用程序

2023-04-14 08:10:59

asyncawait

2014-07-15 10:31:07

asyncawait

2024-11-13 01:00:18

asyncawait?編程

2016-11-22 11:08:34

asyncjavascript

2021-01-19 05:30:55

C# 8異步流IEnumerable

2018-12-19 18:40:28

JavaScriptes6 前端

2011-02-22 08:49:16

.NET同步異步

2011-02-22 09:09:21

.NETAsync CTP異步

2012-07-22 15:59:42

Silverlight

2021-07-20 10:26:12

JavaScriptasyncawait
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 精品亚洲国产成av人片传媒 | 国产在线精品一区 | 成人a免费| 妹子干综合 | 午夜三区 | 国产精品久久亚洲7777 | 男女精品久久 | 黄色av免费网站 | 欧美日韩美女 | 黄在线免费观看 | 日韩欧美福利视频 | 亚洲成人一级 | 亚洲欧美高清 | 天天躁日日躁性色aⅴ电影 免费在线观看成年人视频 国产欧美精品 | 一区中文字幕 | 欧美日韩一区在线观看 | 五月激情久久 | 久久在线视频 | 免费国产视频在线观看 | 国产精品久久久久久影视 | 欧美激情在线精品一区二区三区 | www.久久99 | 国产精品久久久久久久午夜 | 欧美福利 | 亚洲高清久久 | 国产精品视频一二三区 | 亚洲高清在线免费观看 | 国产精品无码久久久久 | 蜜桃av一区二区三区 | 亚洲精品一区二区三区中文字幕 | 久久这里只有精品首页 | 日本免费在线 | 91av大全| 国产一级在线 | 亚洲a一区二区 | 亚洲在线高清 | 久久精品国产99国产精品亚洲 | 亚洲丝袜天堂 | 日韩在线视频播放 | 精品国产精品一区二区夜夜嗨 | 欧美精品综合在线 |