82

私は現在、Stephen Clearyによる「Concurrency in C#Cookbook」を読んでいますが、次のようなテクニックに気付きました。

var completedTask = await Task.WhenAny(downloadTask, timeoutTask);  
if (completedTask == timeoutTask)  
  return null;  
return await downloadTask;  

downloadTaskhttpclient.GetStringAsyncへの呼び出しです。timeoutTaskTask.Delayを実行しています。

タイムアウトしなかった場合、downloadTaskはすでに完了しています。タスクが既に完了しているため、なぜdownloadTask.Resultを返さずにもう一度待つ必要があるのでしょうか。


  • ここには少し文脈が欠けています、そして、人々がその本にすぐにアクセスできないのでなければ、あなたはそれを含める必要があるだろう。何ですかdownloadTaskそしてtimeoutTask?彼らは何をしますか? - Mike Perrenoud
  • ここでは、正常に完了したかどうかの実際の確認が表示されません。タスクは非常にうまく失敗する可能性があり、その場合の動作は意志異なる (AggregateExceptionResult対最初の例外ExceptionDispatchInfoawait) Stephen Toubの「.NET 4.5でのタスク例外処理」で詳しく説明しています。blogs.msdn.com/b/pfxteam/archive/2011/09/28/…) - Kirill Shlenskiy
  • あなたはこれを答えにするべきです@KirillShlenskiy - Carsten
  • @MichaelPerrenoudご存知のとおり、ご指摘いただきありがとうございます。質問を編集します。 - julio.g

2 답변


110

ここにはすでにいくつかの良い答え/コメントがありますが、ただ参加するには...

私が好む理由は2つあります。awaitオーバーResult(またはWait) 1つは、エラー処理が異なることです。awaitで例外をラップしません。AggregateException。理想的には、非同期コードは決して対処する必要はないはずです。AggregateException特に明記しない限り、望むに。

第二の理由はもう少し微妙です。私のブログで(そして本の中で)説明しているように、Result/Waitデッドロックを引き起こす可能性があります、そしてで使用すると、さらに微妙なデッドロックが発生する可能性があります。async方法。だから、コードを読んでいると、ResultまたはWait、それは即時の警告フラグです。のResult/Waitあなたがいる場合だけ正しいです絶対確実タスクは既に完了していること。一見して(実世界のコードで)これを見るのが難しいだけでなく、それはまたコード変更に対してより脆弱です。

それは言うことではありませんResult/Waitすべき決して利用される。私は自分のコードでこれらのガイドラインに従います。

  1. アプリケーション内の非同期コードでしか使用できないawait
  2. (ライブラリ内の)非同期ユーティリティコードは時折使用することができますResult/Waitコードが本当にそれを要求しているなら。そのような用法はおそらくコメントがあるはずです。
  3. 平行タスクコードは使用することができますResultそしてWait

(1)がはるかに一般的なケースであることに注意してください。await他の場合は一般的な規則の例外として扱ってください。


  • '結果'を使用してデッドロックに遭遇しました。 'ではなく'私たちのプロジェクトで。めちゃくちゃになった部分はコンパイルエラーがなく、あなたのコードはしばらくすると不安定になります。 - Ahmad Mousavi
  • <理想的には、非同期コードが特にAggregateExceptionを処理する必要がないようにする必要があるのはなぜですか。 - vcRobe
  • @vcRobeという理由でawaitを防ぐAggregateExceptionラッパー。AggregateException非同期プログラミングではなく、並列プログラミング用に設計されています。 - Stephen Cleary
  • > "タスクが既に完了していることが確実に確認されている場合のみ、[待機]は正しいです。 ....それではそれはなぜウェイトと呼ばれるのですか? - Ryan The Leach
  • @RyanTheLeach:の本来の目的Waitに参加することでした動的タスク並列処理 Taskインスタンス。非同期を待つためにそれを使うTaskインスタンスは危険です。マイクロソフトは、新しい「約束」の導入を検討しました。タイプ、しかし既存のものを使用することを選びましたTask代わりに既存のものを再利用することのトレードオフTask非同期タスクのタイプは、非同期コードで使用してはいけないいくつかのAPIを使用することになります。 - Stephen Cleary

7

これは理にかなっている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。それは後者が投げるということですAggregateExceptionコメントで@KirillShlenskiyが指摘したように、これは元の例外をすべてラップします。前者は元の例外を再度スローするだけです。

どちらの場合も、例外を処理する場所はどこでも、確認する必要があります。AggregateExceptionとにかく、その内部の例外は、エラーの原因に到達するために。

リンクされた質問


関連する質問

最近の質問