タスクの結果にアクセスするときに C# 非同期メソッドがハングする理由
非同期プログラミングに C# の async
および await
キーワードを使用する場合、特定の構造によって予期しない動作やデッドロックが発生する可能性があります。
次のシナリオを考えてみましょう。多層アプリケーションは、SQL クエリを非同期に実行して結果を返す拡張データベース ユーティリティ メソッド ExecuteAsync
を使用します。中間層メソッド GetTotalAsync
は ExecuteAsync
を呼び出してデータを取得し、結果を asyncTask
変数に保存します。最後に、UI 操作は asyncTask.Result
を使用して結果に同期的にアクセスしようとします。ただし、アプリケーションは無期限にハングします。
デッドロックの原因
問題は、GetTotalAsync
メソッドで await
を使用することで発生します。デフォルトでは、非同期メソッドの継続は、メソッドを開始したのと同じ SynchronizationContext
でディスパッチされます。この場合、UI スレッドで await
を使用すると、継続 (return result;
) も UI スレッドで実行されるようにスケジュールされます。
asyncTask.Result
が UI スレッドで呼び出されると、タスクの完了時にスレッドがブロックされます。ただし、UI スレッドでスケジュールされた継続は、asyncTask.Result
が完了するまで実行できません。これにより、どちらのスレッドも実行を続行できないデッドロックが発生します。
解決策
このデッドロックを解決するには、いくつかの方法があります:
1. 非同期キーワードを削除します:
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 中国語 Web サイトの他の関連記事を参照してください。