C#非同步方法在存取Task結果時掛起的原因
使用C#的async
和await
關鍵字進行非同步程式設計時,某些結構可能會導致意外行為和潛在的死鎖。
考慮以下場景:一個多層應用程式使用擴充的資料庫實用程式方法ExecuteAsync
,該方法非同步執行SQL查詢並傳回結果。中間層方法GetTotalAsync
呼叫ExecuteAsync
來檢索資料並將結果儲存在asyncTask
變數中。最後,UI操作嘗試使用asyncTask.Result
同步存取結果。但是,應用程式無限期掛起。
死鎖的原因
問題源自於在GetTotalAsync
方法中使用await
。預設情況下,非同步方法的延續在啟動方法的相同SynchronizationContext
上調度。在這種情況下,當在UI執行緒上使用await
時,延續(return result;
)也計劃在UI執行緒上運行。
當在UI執行緒上呼叫asyncTask.Result
時,它會在Task完成時阻塞執行緒。但是,在UI執行緒上調度的延續直到asyncTask.Result
完成才能執行。這會建立一個死鎖,兩個執行緒都無法繼續執行。
解
為了解決這個死鎖,有幾個方法:
1. 刪除Async關鍵字:
消除await
的使用,並將ExecuteAsync
和GetTotalAsync
方法重寫為不等待的純非同步方法:
<code class="language-csharp">public static Task<T> ExecuteAsync<T>(this OurDBConn dataSource, Func<OurDBConn, T> function) { // ... (代码保持不变) } public static Task<ResultClass> GetTotalAsync(...) { // ... (代码保持不变) }</code>
2. 使用ConfigureAwait:
使用ConfigureAwait(false)
指定延續不應在UI執行緒上調度:
<code class="language-csharp">public static async Task<ResultClass> GetTotalAsync(...) { var resultTask = this.DBConnection.ExecuteAsync<ResultClass>( ds => ds.Execute("select slow running data into result")); return await resultTask.ConfigureAwait(false); }</code>
注意,這種方法需要在所有可能導致死鎖的await
操作上明確指定ConfigureAwait(false)
。
3. 使用SynchronizationContext:
為非同步操作建立一個特定的SynchronizationContext
,並確保所有await
操作都使用該上下文,從而防止與UI執行緒發生衝突。
以上是為什麼我的非同步 C# 方法在存取任務結果時掛起?的詳細內容。更多資訊請關注PHP中文網其他相關文章!