task.Resultと同じ完了したタスクを待ちます。


117

私は現在、Stephen Clearyの「C#クックブックの並行性」を読んでおり、次のテクニックに気づきました。

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

downloadTaskへの呼び出しでhttpclient.GetStringAsynctimeoutTask実行中Task.Delayです。

タイムアウトしなかった場合downloadTaskは、すでに完了しています。downloadTask.Resultタスクがすでに完了している場合、を返すのではなく、2回目の待機を行う必要があるのはなぜですか?


3
ここには少し不足しているコンテキストがあり、人々がその本に簡単にアクセスできない限り、あなたはそれを含める必要があります。何ですdownloadTasktimeoutTask?彼らは何をしますか?
Mike Perrenoud 2014

7
正常に完了したかどうかの実際のチェックはここには表示されません。タスクに障害が発生している可能性が非常に高く、その場合は動作異なります(AggregateExceptionwith Resultvs first exception via ExceptionDispatchInfowith await)。Stephen Toubの「.NET 4.5でのタスク例外処理」でより詳細に議論されています:blogs.msdn.com/b/pfxteam/archive/2011/09/28/…
Kirill Shlenskiy 14

あなたはこれを答えにするべきです@KirillShlenskiy
Carsten

@MichaelPerrenoudあなたは正しい、気づいてくれてありがとう、私は質問を編集します。
julio.g 2014

回答:


160

ここにはすでにいくつかの良い答え/コメントがありますが、ただチャイムするために...

awaitResult(またはWait)よりも好む理由は2つあります。1つ目は、エラー処理が異なることです。await例外をでラップしませんAggregateException。理想的には、非同期コードが対処する必要はありませんAggregateException、それは特にない限り、まったく望んでいるに。

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

それはそれと言うことはありませんResult/がWaitなければならない決して使用しないこと。私は自分のコードでこれらのガイドラインに従います:

  1. アプリケーションの非同期コードで使用できるのはだけawaitです。
  2. (ライブラリー内の)非同期ユーティリティー・コードは、コードが実際にそれを必要とする場合、時々Result/を使用できますWait。そのような使用法にはおそらくコメントが必要です。
  3. 並列タスクコードはResultおよびを使用できますWait

(1)ははるかに一般的なケースであるため、awaitどこでも使用し、他のケースを一般規則の例外として扱う傾向があることに注意してください。


プロジェクトでは、「待機」ではなく「結果」を使用してデッドロックに遭遇しました。めちゃくちゃな部分はコンパイルエラーがなく、しばらくするとコードが不安定になります。
Ahmad Mousavi

@Stephenに、「理想的には、非同期コードは特に必要がない限り、AggregateExceptionをまったく処理する必要がない」と説明してください
vcRobe

4
@vcRobe ラッパーをawait防止するためAggregateExceptionAggregateException非同期プログラミングではなく、並列プログラミング用に設計されました。
Stephen Cleary 2017

2
>「待機は、タスクがすでに完了していることが確実である場合にのみ正しいです。」....では、なぜそれが待機と呼ばれるのですか?
ライアンザリーチ

4
@RyanTheLeach:の本来の目的はWaitDynamic Task Parallelism Taskインスタンスに参加することでした。これを使用して非同期Taskインスタンスを待機することは危険です。マイクロソフトは新しい「プロミス」タイプの導入を検討しましたが、Task代わりに既存のものを使用することを選択しました。Task非同期タスクで既存のタイプを再利用することのトレードオフは、単に非同期コードで使用されるべきではないいくつかのAPIになってしまうことです。
スティーブンクリアリー2018年

12

これtimeoutTaskがの製品である場合、これは理にかなっていTask.Delayます。本でそれが何であるかを私は信じています。

Task.WhenAny戻りTask<Task>インナータスクは、あなたが引数として渡されたものの一つであり、。次のように書き直すことができます。

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、エラーの原因を特定するために、その内部例外を確認する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.