简单说说async/await
小明用async/await寫了幾年的異步方法,但總沒(méi)有完全理解里面的機(jī)制,他決定去請(qǐng)教鄰居小花。
小花聽了小明的描述后說(shuō):首先你要明白異步的根本是什么?大白話解釋異步就是:拉一個(gè)人(線程)幫著做一些耗時(shí)的事(下載、讀寫數(shù)據(jù)庫(kù)等),自己先做別的事了(退出線程),等做好了和我說(shuō)下,我再繼續(xù)做后面的事(恢復(fù)上下文)。
小花看到小明還沒(méi)有聽明白,就說(shuō):我舉個(gè)簡(jiǎn)單例子幫你理解吧,假如有兩個(gè)方法A和B,A調(diào)用B方法,B方法是一個(gè)異步方法,這時(shí)A不等待B執(zhí)行完,如圖:
現(xiàn)在兩個(gè)方法被分隔幾個(gè)小塊,await關(guān)鍵字其實(shí)就用來(lái)隔開同步和異步,上面的方法執(zhí)行流程如下:
A調(diào)用B方法后,B方法在未執(zhí)行到await之前還是同步方法,比如輸出Sub1還是在當(dāng)前線程中執(zhí)行,當(dāng)方法遇到await后,就會(huì)把a(bǔ)wait后的方法放到新的線程中執(zhí)行,當(dāng)前線程則退出函數(shù),由于調(diào)用的地方并沒(méi)有await,則主線程會(huì)繼續(xù)執(zhí)行并輸出Part2,然后結(jié)束。等新線程中Thread.Sleep(5000)執(zhí)行完后,會(huì)執(zhí)行到Console.Write("Sub2");這一行代碼會(huì)回到原來(lái)的線程執(zhí)行,其實(shí)調(diào)用線程在遇到await時(shí)會(huì)捕獲當(dāng)前線程的執(zhí)行上下文,然后給到新線程,新線程在執(zhí)行完耗時(shí)操作后,會(huì)判斷之前捕獲到的執(zhí)行上下方是否為null,如果不為null,則會(huì)在上下文中恢復(fù)并執(zhí)行后面的方法,其實(shí)就是通過(guò)Tak的ContineWith方法注冊(cè)回調(diào),如圖:
小明好像聽懂了一些說(shuō):現(xiàn)在A方法調(diào)用DoSomethingAsync()并沒(méi)有等待,如果A方法需要這個(gè)方法執(zhí)行完才能繼續(xù)執(zhí)行,是不是要在DoSomethingAsync()前面加上await?小花回答是,并說(shuō):方法只要遇到await,就會(huì)把后面的方法給新線程執(zhí)行,然后退出線程去執(zhí)行別的方法,等新線程執(zhí)行完后再通知當(dāng)前線程恢復(fù)上下文繼續(xù)執(zhí)行,如圖:
小明又問(wèn):你說(shuō)異步方法執(zhí)行完后,后面的方法會(huì)在原來(lái)的線程中恢復(fù)并執(zhí)行,如果我還想在新線程中繼續(xù)執(zhí)行剩下的代碼,要怎么辦呢?小花說(shuō)問(wèn)的好,await調(diào)用新線程執(zhí)行耗時(shí)操作時(shí)默認(rèn)會(huì)捕獲當(dāng)前上下文,如果不想捕獲,則可以調(diào)用ConfigAwait(false)方法,如圖:
執(zhí)行流程如下:
小花補(bǔ)充到,上線提到的線程1、線程2、線程3等不一定準(zhǔn)確,因?yàn)楫惒降幕卣{(diào)使用的是線程池中的線程,所以回調(diào)有可能還在原來(lái)線程中執(zhí)行,這個(gè)主要看操作系統(tǒng)的調(diào)度。
小明滿意地點(diǎn)點(diǎn)頭又問(wèn):我經(jīng)常聽同事說(shuō)用異步方法會(huì)死鎖,這又是為什么呢?小花聽了說(shuō),他們肯定是在調(diào)用異步方法的時(shí)候使用.Result(),如圖:
小花指著圖解釋說(shuō):上面的代碼task.Result()會(huì)阻塞線程并等待task返回結(jié)果,DoSomethingAsync方法在執(zhí)行完Thread.Sleep(5000)后,發(fā)現(xiàn)捕獲到的上下文不為空,則會(huì)嘗試將Console.Write("Sub2")這行代碼交由調(diào)用線程去執(zhí)行,而這時(shí)調(diào)用線程還在阻塞等待,就這樣互相卡著對(duì)方,從而造成了死鎖,如圖:
小明點(diǎn)了點(diǎn)頭又問(wèn):那要怎么避免呢?小花說(shuō)出現(xiàn)這種情況也和框架有關(guān),像WinForm為了讓所有UI操作都在主線程中執(zhí)行,就添加了一個(gè)SynchronizationContext類實(shí)例用以表示當(dāng)前上下文,而像控制臺(tái)等項(xiàng)目這個(gè)SynchronizationContext實(shí)例默認(rèn)為null,所以即使使用.Result也不會(huì)死鎖。但最好使用異步的時(shí)候不要用.Result,可以使用ConfigAwait(false)指明不捕獲上下文,或所有的方法全部異步到底。
小明聽完,拜別了小花,回到了自己的隔間。
總結(jié)
以上是生活随笔為你收集整理的简单说说async/await的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 研发协同平台数据库死锁处理及改进
- 下一篇: Newtonsoft 六个超简单又实用的