現在、Stephen Clearyによる「C#Cookbookの同時実行」を読んでいますが、次の手法に気付きました。
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask
はhttpclient.GetStringAsync
への呼び出しであり、timeoutTask
はTask.Delay
を実行しています。
タイムアウトしなかった場合、downloadTask
はすでに完了しています。タスクが既に完了している場合、なぜdownloadTask.Result
を返す代わりに2回目の待機が必要なのですか?
ここにはすでにいくつかの良い回答/コメントがありますが、チャイムに...
await
(またはResult
)よりもWait
を好む理由は2つあります。 1つ目は、エラー処理が異なることです。 await
は、例外をAggregateException
にラップしません。理想的には、非同期コードは、特にwantsしない限り、AggregateException
を処理する必要はありません。
2番目の理由はもう少し微妙です。私のブログ(および本)で説明しているように、 Result
/Wait
はデッドロックを引き起こす可能性があります 、および async
メソッドで使用するとさらに微妙なデッドロックを引き起こす可能性があります 。したがって、コードを読んでいるときにResult
またはWait
が表示された場合、それは即時の警告フラグです。 Result
/Wait
は、タスクが既に完了していることを確実に確信している場合にのみ正しいです。これは(実際のコードでは)一目でわかりにくいだけでなく、コードの変更により脆弱です。
それは、Result
/Wait
をneverを使用すべきだということではありません。私は自分のコードでこれらのガイドラインに従います:
await
のみを使用できます。Result
/Wait
を使用することがあります。このような使用法にはおそらくコメントが必要です。Result
およびWait
を使用できます。(1)は一般的なケースであるため、await
をすべての場所で使用し、他のケースを一般規則の例外として扱う傾向があることに注意してください。
timeoutTask
がTask.Delay
の製品である場合、これは理にかなっています。
Task.WhenAny
はTask<Task>
を返します。ここで、内部タスクは引数として渡したタスクの1つです。次のように書き換えることができます。
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
いずれの場合も、downloadTask
はすでに完了しているため、return await downloadTask
とreturn downloadTask.Result
にはわずかな違いがあります。 @KirillShlenskiyがコメントで指摘したように、後者は元の例外をラップするAggregateException
をスローするという点にあります。前者は元の例外を再スローするだけです。
いずれの場合でも、例外を処理する場合は常に、AggregateException
とその内部例外をチェックして、エラーの原因を特定する必要があります。