【轉(zhuǎn)載】async await 異步編程詳解
http://www.rzrgm.cn/rosanshao/p/3728108.html#!comments
http://www.rzrgm.cn/wisdomqq/archive/2012/03/29/2417723.html
寫在前面的話,很久沒(méi)有寫B(tài)log了,不對(duì),其實(shí)一致就沒(méi)有怎么寫過(guò).今天有空,我也來(lái)寫一篇Blog
隨著.Net4.5的推出,一種新的編程方式簡(jiǎn)化了異步編程,在網(wǎng)上時(shí)不時(shí)的也看到各種打著Asp.Net異步編程的口號(hào),如何提高性能,如何提高吞吐率!
好多文章都說(shuō)得不清楚,甚至是錯(cuò)誤的.只看到了一些表象,混淆概念.希望這篇文章能夠能夠?qū)σ徊糠秩死斫釧sp.net異步編程模型.
本文的重點(diǎn)是理解Asp.net異步如何提高吞吐率,提高性能.當(dāng)然提高性能的不單是異步,有很多方式,多線程等等.
1基礎(chǔ)知識(shí),談一個(gè)初學(xué)者不容易理解的基礎(chǔ)知識(shí),這個(gè)基礎(chǔ)知識(shí),很不基礎(chǔ)的哦
先看這個(gè)代碼
ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Asp.net有二類線程,1類就是工作線程,另一類是IO線程,也有叫完成端口線程.簡(jiǎn)單說(shuō)一下,工作線程:處理普通請(qǐng)求的線程,平常代碼中運(yùn)用得最多的線程.
這個(gè)線程是有限的,是根CPU的個(gè)數(shù)相關(guān)的.IO線程,就是比如與文件讀寫,網(wǎng)絡(luò)操作等就可以異步實(shí)現(xiàn)真正意義的性能提升[異步].
這個(gè)IO線程如果沒(méi)有專門處理,通常情況下也是沒(méi)有處理的,這個(gè)IO線程基本上都是空閑的
就是可以使用IO線程來(lái)代替工作線程,因?yàn)樘幚碛脩粽?qǐng)求的是工作線程,是有限的,比較珍貴的。
2ThreadPool,Task這二個(gè)其實(shí)都是線程,對(duì)于Asp.net來(lái)說(shuō),代碼沒(méi)有做特殊的處理通常都是工作線程,線程池里的線程
Thread這個(gè)是底層的線程,沒(méi)有做任何封裝,直接使用,創(chuàng)建這個(gè)線程比較費(fèi)時(shí),同時(shí)不容易重用.
3async/await一個(gè)新的語(yǔ)法糖,一個(gè)簡(jiǎn)化方式的異步編程模型,值得推薦.有了這個(gè)后,我們的異步編程模型變得簡(jiǎn)單,優(yōu)雅--這個(gè)和Task關(guān)系很緊密的,如何...自己去實(shí)踐
以上幾個(gè)概念了解后,我們就是使用最佳實(shí)踐,提高性能,吞吐率了
下面給出一個(gè)WebApi的示例
public async Task<string> Get()
{
return await GetArticleContentAsync();
}
private async Task<string> GetArticleContentAsync()
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://www.asp.net");
var buffer = await response.Content.ReadAsByteArrayAsync();
return Encoding.UTF8.GetString(buffer);
}
}
這個(gè)代碼,看起來(lái)和網(wǎng)上其他的Blog差不多,但這樣的方式對(duì)于asp.net異步,提升吞吐率的效果是最佳的,第1,使用IO端口,在處理網(wǎng)絡(luò)請(qǐng)求的時(shí)候[從http://www.asp.net獲取數(shù)據(jù)的時(shí)候]
把當(dāng)時(shí)處理的工作線程返回給了線程池,讓其可以處理其他用戶的請(qǐng)求,在從網(wǎng)絡(luò)www.asp.net獲取數(shù)據(jù)的時(shí)候,只占用了一個(gè)IO線程
現(xiàn)在列出,網(wǎng)上其他Blog的關(guān)于這塊的
public async Task<string> GetArticleContentByNoRigntWayAsync()
{
return await Task.Run(() =>
{
using (var client = new WebClient())
{
return client.DownloadString("http://www.asp.net");
}
});
}
這個(gè)代碼看起來(lái)和上面的代碼沒(méi)有什么區(qū)別,但是這樣代碼和上面的第一種方式是有本質(zhì)的區(qū)別,性能真的有提升嗎?真的能提升吞吐率嗎?好多開發(fā)也是這樣使用的
我先在這兒給出答案,這樣的方式[使用GetArticleContentByNoRigntWayAsync],是不太可能提升性能的,特別是在Asp.net環(huán)境中
這兒的確用于了異步,也用到了Task,線程池.僅僅用到了而已
想知道為什么沒(méi)有提升性能,沒(méi)有提高吞吐率,需要各位客觀的支持
接著昨天沒(méi)有說(shuō)完的,繼續(xù)說(shuō)!
把同步方法封閉成異步,在Asp.net中只會(huì)占用線程池的線程池,同時(shí)也可能會(huì)造成線程間的切換,至于線程池的切換耗時(shí)不,我不清楚,但是已經(jīng)在關(guān)注性能問(wèn)題了,那我們就
應(yīng)該避免線程切換,切換總比不切換耗時(shí),對(duì)吧
我們要使用并行計(jì)算,我們直接使用使用同步,再加上幾個(gè)Task就可以了,如果只有一個(gè)Task,又是同步,也沒(méi)有必要.
所以對(duì)于我們使用言我總結(jié)一下
使用異步,就要使用IO線程,充分利用這個(gè)去完成操作
如果沒(méi)有使用IO線程,就直接使用同步會(huì)更好
1異步+IO線程
2直接同步
3并行計(jì)算[充分利用CPU] 同步+二個(gè)以上的Task
a并行期間吞吐率會(huì)下降,如果CPU有空閑的話,可以考慮自己實(shí)現(xiàn)一個(gè)線程池[使用Thread],通常不易寫穩(wěn)定
b[建議]把ThreadPool的默認(rèn)數(shù)量改大些,前提還是CPU有空閑的話
經(jīng)典評(píng)論
服務(wù)器每個(gè)請(qǐng)求都會(huì)有個(gè)線程,對(duì)線程池而言,線程占用的時(shí)間越短,性能就越好,這就是async/await的意義。
第一種,走異步IO,工作線程回歸線程池,等異步IO線程,執(zhí)行完畢再取一個(gè)線程執(zhí)行先前定義的異步回調(diào)方法。
第二種,雖然task(內(nèi)封裝了一個(gè)線程,以A命名)異步了,可是這個(gè)task內(nèi)部還是同步的,task的異步的實(shí)質(zhì)是task內(nèi)線程A同步執(zhí)行IO,同步IO操作完畢后,線程A取另一個(gè)線程,執(zhí)行回調(diào)方法。
我個(gè)人理解用異步IO,更多的是避免阻塞的不好體驗(yàn),并行的性能反而是次要的。
第二種雖然是同步IO,但也可以避免阻塞的問(wèn)題
線程之前的切換是有成本的,當(dāng)然用第一種的更好
總只 ,網(wǎng)絡(luò)和IO請(qǐng)求,交給硬件,然后調(diào)用的線程就回收了,當(dāng)硬件處理或網(wǎng)絡(luò)請(qǐng)求完畢,會(huì)重新有另一個(gè)線程來(lái)處理返回結(jié)果。我是這樣理解的。 單純的使用task來(lái)異步完成任務(wù),只是不會(huì)阻塞,并不會(huì)減輕cpu的壓力,反而會(huì)增加。望樓主指教學(xué)習(xí)。


浙公網(wǎng)安備 33010602011771號(hào)