C# ConfigureAwait(false)在UI程式中避免了死鎖
阿新 • • 發佈:2021-01-02
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 staticasync 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執行緒裡不去獲取非同步任務的結果
執行結果: