C 中async的死鎖分析和解決方案

2022-02-26 20:36:20 字數 4232 閱讀 6781

如果你開發乙個簡單的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_clickawait時候發生了兩件事:

然後在第4步,當button1_click中去獲取上面這個task返回值的時候出現了死鎖,button1_clickgetcontentasync相互等待:

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無響應的問題。

getcontentasyncawait之後可以更新ui介面。

缺點:button1_click改為了非同步,對原來的方法有侵入性,甚至會改變整個呼叫鏈的行為,我最討厭這點了。

上面的死鎖通常會發生在下面兩個地方

windows forms的ui執行緒中呼叫了非同步的方法。

非同步方法實現者

分開提供同步和非同步方法

只是自己做一些事,不需要bind到呼叫執行緒上的需要盡量.configawait(continueoncapturedcontext:false)

對於非同步方法使用者

看看是否提供了同步方法

考慮是否有機會將自己的**轉為非同步**

實在不行放到threadpool中去執行

如果加上異常處理,那麼async/await會變得更加複雜,因為非同步方法在非同步執行,所以可以放到不同的執行緒中,那麼如果出現了異常會怎麼樣?簡單來說:

非同步**中的異常如果存在tasktask被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 有些系統資源是不可搶占的,當某個程序已獲得這種資...