[解決済み] 非同期コードからの非同期メソッドの呼び出し
質問
.NET 3.5 で構築された API サーフェスを持つライブラリを更新している最中です。その結果、すべてのメソッドは同期です。API を変更する (つまり、戻り値をタスクに変換する) ことはできません。そのため、非同期メソッドを同期的に呼び出すにはどうしたらよいかという問題が残されています。これは、ASP.NET 4、ASP.NET Core、および .NET/.NET Core コンソール アプリケーションのコンテキストでの話です。
私は十分に明確ではなかったかもしれません。状況は、非同期を認識していない既存のコードを持っており、System.Net.Http や AWS SDK などの非同期メソッドのみをサポートする新しいライブラリを使用したい、というものです。したがって、私はギャップを埋める必要があり、同期的に呼び出すことができるコードを持つことができ、その後、他の場所で非同期メソッドを呼び出すことができます。
私はたくさん読みましたし、これは何度も質問され、回答されています。
非同期処理での同期待ち、ここでWait()がプログラムをフリーズさせる理由
非同期Task<T>メソッドを同期的に実行するにはどうしたらよいでしょうか。
C#でsynchronousメソッドから非同期メソッドを呼び出すには?
問題は、ほとんどの回答が異なっていることです! 私が見た最も一般的なアプローチは、.Resultを使用することですが、これはデッドロックになる可能性があります。しかし、デッドロックを回避し、優れたパフォーマンスを発揮し、ランタイムとうまく機能する (タスク スケジューラー、タスク作成オプションなどを尊重するという意味で) 最良のアプローチがどれなのかがわかりません。決定的な答えはあるのでしょうか?最良のアプローチは何でしょうか?
private static T taskSyncRunner<T>(Func<Task<T>> task)
{
T result;
// approach 1
result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 2
result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 3
result = task().ConfigureAwait(false).GetAwaiter().GetResult();
// approach 4
result = Task.Run(task).Result;
// approach 5
result = Task.Run(task).GetAwaiter().GetResult();
// approach 6
var t = task();
t.RunSynchronously();
result = t.Result;
// approach 7
var t1 = task();
Task.WaitAll(t1);
result = t1.Result;
// approach 8?
return result;
}
どのように解決するのですか?
<ブロッククオートというわけで、非同期メソッドを同期的に呼び出すのに最適な方法が残されています。
まず、これはやってもいいことです。Stack Overflowでは、具体的なケースを無視して一律に悪魔の所業として指摘するのが一般的なので、このように記載しています。
ずっと非同期である必要はない 正しさのため . 同期させるために非同期のものをブロックすることは、重要かもしれないし全く関係ないかもしれない性能上のコストがあります。それは具体的なケースに依存します。
デッドロックは、2 つのスレッドが同じシングルスレッド同期コンテキストに同時に入ろうとすることから発生します。これを確実に回避する技術は、ブロッキングによって引き起こされるデッドロックを回避することができます。
あなたのコードスニペットでは、すべての呼び出しが
.ConfigureAwait(false)
の呼び出しは、戻り値が期待できないため、無意味です。
ConfigureAwait
は、待ち受けにしたときに要求された動作をする構造体を返します。その構造体が単にドロップされただけなら、何もしません。
RunSynchronously
は、すべてのタスクがそのように処理できるわけではないので、使用するのは無効です。この方法はCPUベースのタスクのためのものであり、特定の状況下では動作しないことがあります。
.GetAwaiter().GetResult()
とは異なり
Result/Wait()
を模倣している点で
await
例外伝播の振る舞いを模倣しています。それを望むか望まないかを決める必要があります。(だから、その動作が何であるか調べてください。ここで繰り返す必要はありません。) もしタスクが一つの例外を含んでいるならば
await
エラー動作は通常便利で、デメリットはほとんどありません。複数の例外がある場合、例えば失敗した
Parallel
ループで複数のタスクが失敗した場合など、複数の例外が発生する場合は
await
は最初の例外を除いて全ての例外を落とします。そのため、デバッグがしにくくなります。
これらのアプローチはすべて似たようなパフォーマンスです。これらは、何らかの方法で OS イベントを割り当て、その上でブロックします。これが高価な部分です。他の機械はそれに比べればむしろ安いものです。どのアプローチが絶対的に一番安いかはわかりません。
例外がスローされる場合、それが最も高価な部分となる予定です。.NET 5 では、例外は高速な CPU でせいぜい 200,000 件/秒の割合で処理されます。ディープスタックは遅く、タスク機械は例外を再スローする傾向があり、そのコストは倍増します。例外を投げ直さずにタスクでブロックする方法があります。
task.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously).Wait();
.
個人的に好きなのは
Task.Run(() => DoSomethingAsync()).Wait();
パターンは、デッドロックを断固として回避し、シンプルで、かつ
GetResult()
が隠すかもしれない例外を隠さないからです。しかし
GetResult()
を使うこともできます。
関連
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み] async」と「await」の使い方とタイミング
-
[解決済み] C#で同期メソッドから非同期メソッドを呼び出すには?
-
[解決済み] Reduxの非同期フローになぜミドルウェアが必要なのか?
-
[解決済み] 非同期Task<T>メソッドを同期的に実行するにはどうしたらいいですか?
-
[解決済み] 私のインターフェースがTaskを返さなければならない場合、操作不要の実装を持つための最良の方法は何ですか?
-
[解決済み] C#でawaitを使わずに非同期メソッドを安全に呼び出す方法
-
[解決済み] Task.Runの正しい使い方とasync-awaitだけの使い方
-
[解決済み】非同期プログラミングとマルチスレッドの違いは何ですか?
-
[解決済み】非同期処理の待ち時間、Wait()でプログラムがフリーズする原因はここにある
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】プログラム実行中に1秒待つ
-
[解決済み] 保護レベルによりアクセス不能になりました。
-
[解決済み】C# ASP.NET使用時に「WebClientのリクエスト中に例外が発生しました。
-
[解決済み】C# - パスに不正な文字がある場合
-
[解決済み】ファイルへの読み書きの際に共有違反のIOExceptionが発生する C#
-
[解決済み】Linq 構文 - 複数列の選択
-
[解決済み】2つ(またはそれ以上)のリストを1つに統合する(C# .NETで
-
[解決済み] C#で同期メソッドから非同期メソッドを呼び出すには?
-
[解決済み] 非同期Task<T>メソッドを同期的に実行するにはどうしたらいいですか?
-
[解決済み】非同期処理の待ち時間、Wait()でプログラムがフリーズする原因はここにある