1. 程式人生 > 實用技巧 >C# ConfigureAwait(false)在UI程式中避免了死鎖

C# ConfigureAwait(false)在UI程式中避免了死鎖

ConfigureAwait(false):當 await 等待完成時,它會嘗試線上程池上下文中執行await之後的程式碼

ConfigureAwait(true):當 async 方法內的 await 執行完成時,它會嘗試獲取之前呼叫者執行緒所在的上下文執行方法的剩餘部分

在Winform程式中沒有使用ConfigureAwait(false)導致的死鎖示例:

新建一個winform桌面程式,頁面如下

在button1的單擊處理事件程式碼如下:

 1         private void button1_Click(object sender, EventArgs e)
 2         {
3 MessageBox.Show($"開始執行,ThreadId:{Thread.CurrentThread.ManagedThreadId}", "button1_Click"); 4 Test(); 5 MessageBox.Show($"執行結束,ThreadId:{Thread.CurrentThread.ManagedThreadId}", "button1_Click"); 6 } 7 8 public static void Test() 9 {
10 var task = XXXAsync(); // 開啟非同步任務,比如下載某個資源 11 var resuslt = task.Result; // 獲取非同步任務的結果顯示給使用者,這裡會阻塞當前ui執行緒直到非同步執行緒返回結果 12 MessageBox.Show($"XXXAsync執行結束顯示非同步任務結果:{resuslt},ThreadId:{Thread.CurrentThread.ManagedThreadId}", "Test"); 13 } 14 15 private static
async Task<string> XXXAsync() 16 { 17 await Task.Delay(1000); // 這裡會導致死鎖 18 //await Task.Delay(1000).ConfigureAwait(false); // 這樣就不會死鎖 19 20 MessageBox.Show($"XXXAsync執行結束,ThreadId:{Thread.CurrentThread.ManagedThreadId}", "XXXAsync"); 21 22 return "XXXAsync.Result"; 23 }

執行程式:

發生死鎖後,無法通過關閉按鈕關閉程式了;

死鎖分析如下:

預設情況下,當 Wait() 未完成的 Task 時,會捕獲當前執行緒(此時是ui主執行緒)上下文,在 Task(即Task.Delay(1000)) 完成時使用該ui主執行緒上下文恢復方法的執行。 當 async 方法內的 await 執行完成時,它會嘗試獲取呼叫者執行緒(這時是ui主執行緒)所在的上下文執行方法的剩餘部分, 但是該上下文已含有一個執行緒(即Test方法裡的task.Result等待獲取非同步任務結果),該執行緒在等待 async 方法完成。然後它們相互等待對方,然後就沒有然後了,死在那裡

解決方法1:使用ConfigureAwait(false)讓await後面的程式碼嘗試線上程池上下文中執行,不在ui執行緒的同步上下文中執行

執行結果:

解決方法2:ui執行緒裡不去獲取非同步任務的結果

執行結果: