常见的异步方式async 和 await
之前研究過(guò)c#的async和await關(guān)鍵字,幕后干了什么,但是不知道為什么找不到相關(guān)資料了。現(xiàn)在重新研究一遍,順便記錄下來(lái),方便以后查閱。
基礎(chǔ)知識(shí)
async 關(guān)鍵字標(biāo)注一個(gè)方法,該方法返回值是一個(gè)Task、或者Task<TResult>、void、包含GetAwaiter方法的類型。該方法通常包含一個(gè)await表達(dá)式。該表達(dá)式標(biāo)注一個(gè)點(diǎn),將被某個(gè)異步方法回跳到該點(diǎn)。并且,當(dāng)前函數(shù)執(zhí)行到該點(diǎn),將立刻返回控制權(quán)給調(diào)用方。
以上描述了async方法想干的事情,至于如何實(shí)現(xiàn),這里就不涉獵了。
個(gè)人見(jiàn)解
由此可以知道,async 和await關(guān)鍵字主要目的是為了控制異步線程的同步,讓一個(gè)異步過(guò)程,表現(xiàn)得好像同步過(guò)程一樣。
比如async 方法分n個(gè)任務(wù)去下載網(wǎng)頁(yè)并進(jìn)行處理:先await下載,然后立刻返回調(diào)用方,之后的處理就由異步線程完成下載后調(diào)用。這時(shí)候調(diào)用方可以繼續(xù)執(zhí)行它的任務(wù),不過(guò),如果調(diào)用方立刻就需要async的結(jié)果,那么應(yīng)該就只能等待,不過(guò)大多數(shù)情況:他暫時(shí)不需要這個(gè)結(jié)果,那么就可以并行處理這些代碼。
可見(jiàn),并行性體現(xiàn)在await 上,如果await 點(diǎn)和最終的數(shù)據(jù)結(jié)果距離越遠(yuǎn),那么并行度就越高。如果await的點(diǎn)越多,相信也會(huì)改善并行性。
資料顯示,async 和await 關(guān)鍵字并不會(huì)創(chuàng)建線程,這是很關(guān)鍵的一點(diǎn)。他們只是創(chuàng)建了一個(gè)返回點(diǎn),提供給需要他的線程使用。那么線程究竟是誰(shuí)創(chuàng)建?注意await 表達(dá)式的組成,他需要一個(gè)Task,一個(gè)Task并不代表一定要?jiǎng)?chuàng)建線程,也可以是另一個(gè)async方法,但是層層包裹最里面的方法,很可能就是一個(gè)原生的Task,比如await Task.Run(()=>Thread.Sleep(0)); ,這個(gè)真正產(chǎn)生線程的語(yǔ)句,就會(huì)根據(jù)前面那些await點(diǎn),逐個(gè)回調(diào)。
從這點(diǎn)來(lái)看,async 方法,未必就是一個(gè)異步方法,他在語(yǔ)義上更加貼近“非阻塞”, 當(dāng)遇到阻塞操作,立刻用await定點(diǎn)返回,至于其他更深一層的解決手段,它就不關(guān)心了。這是程序員需要關(guān)心的,程序員需要用真正的創(chuàng)建線程代碼,來(lái)完成異步操作(當(dāng)然這一步可由庫(kù)程序員完成)。
注意async的幾個(gè)返回值類型,這代表了不同的使用場(chǎng)景。如果是void,說(shuō)明客戶端不關(guān)心數(shù)據(jù)同步問(wèn)題,它只需要線程的控制權(quán)立刻返回。可以用在ui 等場(chǎng)合,如果是Task,客戶端也不關(guān)心數(shù)據(jù),但是它希望能夠控制異步線程,這可能是對(duì)任務(wù)執(zhí)行順序有一定的要求。當(dāng)然,最常見(jiàn)的是Task<TResult>。
綜上,async和await并不是為了多任務(wù)而設(shè)計(jì)的,如果追求高并發(fā),應(yīng)該在async函數(shù)內(nèi)部用Task好好設(shè)計(jì)一番。在使用async 和await的時(shí)候,只需要按照非阻塞的思路去編寫(xiě)代碼就可以了,至于幕后怎么處理就交給真正的多線程代碼創(chuàng)建者吧。
示范代碼
static async Task RunTaskAsync(int step){for(int i=0; i < step; i++){await Task.Run(()=>Thread.Sleep(tmloop));//點(diǎn)是靜態(tài)的,依次執(zhí)行Thread.Sleep(tm2);}Thread.Sleep(tm3);}//客戶端Task tk= RunTaskAsync(step);Thread.Sleep(tm1);//這一段是并行的,取max(函數(shù),代碼段)最大時(shí)間tk.Wait( );//這里代表最終數(shù)據(jù)為了達(dá)到高度并行,應(yīng)該用真正的多線程代碼:
static async Task RunTaskByParallelAsync(int step){await Task.Run(()=>Parallel.For(0,step,s=>{loop(tmloop);loop(tm2);}));loop(tm3);}并行編碼方法
并行執(zhí)行有幾個(gè)方法,第一個(gè)是創(chuàng)建n個(gè)Task,一起啟動(dòng)。問(wèn)題是怎么處理await點(diǎn)。每個(gè)task寫(xiě)一個(gè)await點(diǎn)是不行的,因?yàn)橛龅降谝粋€(gè)await就立刻返回,而不會(huì)開(kāi)啟所有任務(wù)并行執(zhí)行。因此await不能隨便放。那么如何為一組Task設(shè)定await點(diǎn)呢?可以通過(guò)Task.WhenAll 這個(gè)方法,他會(huì)等待一組Task執(zhí)行完畢返回。
特定情況下,可以用Parallel.For 來(lái)開(kāi)啟一組任務(wù),但是這個(gè)類并沒(méi)有實(shí)現(xiàn)async模式,也就是它會(huì)阻塞當(dāng)前線程,所以需要用一個(gè)Task來(lái)包裹它。
可見(jiàn),非阻塞和并行不完全是一回事。
總結(jié)
以上是生活随笔為你收集整理的常见的异步方式async 和 await的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 卡罗拉车主试驾完比亚迪唐DM-i之后 丰
- 下一篇: 浅谈 DDoS 攻击与防御