如果你開發乙個簡單的windows form程式,點選button去使用async
非同步獲取乙個資料,然後顯示在label上,類似這樣的**
private void button1_click(object sender, eventargs e)
public async task getcontentasync()
當你點選button的時候會發現程式直接卡死了。
c#中的async/await
隱藏了很多的細節,乙個簡單的await
其實讓函式發生了一次重入,重入對於多執行緒**來說其實很正常。但是c#將這些藏了起來。你看上去像乙個函式,其實被分成了兩段,而且執行這兩段**的執行緒還可能不一樣。
上面**真正的執行過程是這樣的:
對於getcontentasync
函式來說,在await
之前其實是同步的**,當await
之後,執行緒直接返回給button1_click
。await
時候發生了兩件事:
然後在第4步,當button1_click
中去獲取上面這個task返回值的時候出現了死鎖,button1_click
和getcontentasync
相互等待:
var content = task.result;
而await http.getstringasync("");
等待當前執行緒(ui執行緒)的同步上下文sychronizationcontext
由於上面兩個方法相互等待,所以產生了死鎖。
getcontentasync
自動捕獲的是當前ui執行緒的同步上下文,通過偷偷的
捕獲當前ui執行緒的同步上下文可以讓你在getcontentasync
方法中await
之後可以更新ui控制項。如果你在getcontentasync
中不需要更新ui控制項,那麼我們就不必捕獲同步上下文,那麼也就不存在這個問題。
修改getcontentasync,讓http.getstringasync("」);
自動捕獲上下文時候捕獲不到。破壞了上面的死鎖條件2。
private void button1_click(object sender, eventargs e)
public async task getcontentasync()
優點:
呼叫方**不需要改變
缺點:呼叫者執行緒(ui執行緒)會在var content = task.result;
阻塞,直到getcontentasync
返回,導致介面在此期間無響應。
如果非同步方法類似http.getstringasync("")
需要更新介面(使用ui執行緒)會出現問題
改的**比較多3行。
windowsformssynchronizationcontext.setsynchronizationcontext(null);可能有***。
修改呼叫方式,將呼叫放到thread pool中,這樣await http.getstringasync("");
的auto capture就不會獲取到當前ui執行緒的synchronizationcontext
,破壞了上面的死鎖條件2。
private void button1_click(object sender, eventargs e)
public async task getcontentasync()
優點:
async
方法不需要改變。
缺點:呼叫者執行緒(ui執行緒)會在var content = task.result;
阻塞,直到getcontentasync
返回,導致介面在此期間無響應。
如果非同步方法類似http.getstringasync("")
需要更新介面(使用ui執行緒)會出現問題
通過configureawait來改變自動捕獲synchronizationcontext行為,破壞了上面的死鎖條件2。
private void button1_click(object sender, eventargs e)
public async taskgetcontentasync()
優點:
呼叫方(caller)不需要改變
避免了此處無用的自動捕獲執行緒上下文。
缺點:呼叫者執行緒(ui執行緒)會在var content = task.result;
阻塞,直到getcontentasync
返回,導致介面在此期間無響應。
如果非同步方法類似http.getstringasync("")
需要更新介面(使用ui執行緒)會出現問題
把當前的事件處理函式也改成async的,這樣破壞了死鎖條件的1。button1_click
不在死等,所以也釋放了上下文。
private async void button1_click(object sender, eventargs e)
public async task getcontentasync()
優點:
async
方法不需要改變。
避免了ui無響應的問題。
getcontentasync
在await
之後可以更新ui介面。
缺點:button1_click
改為了非同步,對原來的方法有侵入性,甚至會改變整個呼叫鏈的行為,我最討厭這點了。
上面的死鎖通常會發生在下面兩個地方
windows forms的ui執行緒中呼叫了非同步的方法。
非同步方法實現者
分開提供同步和非同步方法
只是自己做一些事,不需要bind到呼叫執行緒上的需要盡量.configawait(continueoncapturedcontext:false)
對於非同步方法使用者
看看是否提供了同步方法
考慮是否有機會將自己的**轉為非同步**
實在不行放到threadpool中去執行
如果加上異常處理,那麼async/await
會變得更加複雜,因為非同步方法在非同步執行,所以可以放到不同的執行緒中,那麼如果出現了異常會怎麼樣?簡單來說:
非同步**中的異常如果存在task
或task
被attach到了task物件上
但是async void
例外,由於沒有task物件可以attach,所以attach到了synchronizationcontext
中活躍的執行緒中了。
非同步方法呼叫(呼叫鏈)中的異常,會被aggregate,然後生成乙個aggregateexception
。你可以使用aggexp.flatten()
方法來方便檢視這個呼叫鏈中所有異常--如果有多個的話。
asynclockandfixes
首發:
死鎖問題的出現和解決
是指兩個或者兩個以上的執行緒在執行的過程中,因爭奪資源產生的一種互相等待的現象 舉例 中國人 美國人吃飯案例 正常情況 中國人 筷子兩支 美國人 刀和叉 現在 中國人 筷子一支,刀一把 美國人 筷子一支,叉一把 產生死鎖問題 中國人拿著刀的同時等著美國人把另乙隻筷子給他,美國人拿著一支筷子的同時等著...
死鎖的現象和解決辦法
產生死鎖的根本原因是兩個或者兩個以上執行緒在執行過程中,因爭搶資源而產生相互等待的一種現象。在申請鎖的時候發生了交叉閉環申請。死鎖產生的四個條件 1 互斥。共享資源同時只能被乙個執行緒訪問。2 占有且等待。執行緒t1在取得共享資源a的時候,請求等待資源b的時候並不釋放資源a。3 不可搶占。其他執行緒...
死鎖產生的原因和解決辦法
死鎖的條件 互斥條件 mutual exclusion 資源不能被共享,只能由乙個程序使用。請求與保持條件 hold and wait 程序已獲得了一些資源,但因請求其它資源被阻塞時,對已獲得的資源保持不放。不可搶占條件 no pre emption 有些系統資源是不可搶占的,當某個程序已獲得這種資...