失敗したタスク(例外が設定されているタスク)を待っているときawait
格納された例外を再スローします。格納された例外がAggregateException
それは最初のものを再投げそして残りを捨てるでしょう。
どうやって使えますかawait
そして同時にオリジナルを投げるAggregateException
誤ってエラー情報を紛失しないように
これに対するハックな解決策を考えることはもちろん可能であることに注意してください。await
その後、電話Task.Wait
)私は本当にきれいな解決策を見つけたいです。ここでのベストプラクティスは何ですか?
カスタムのウェイターを使うことを考えましたが、内蔵のTaskAwaiter
完全に再現する方法がわからないという魔法がたくさん含まれています。 TPL型の内部APIを呼び出します。私もしません欲しいですそのすべてを再現するために。
あなたがそれと遊びたいならば、これは短いreproです:
static void Main()
{
Run().Wait();
}
static async Task Run()
{
Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
await Task.WhenAll(tasks);
}
static Task CreateTask(string message)
{
return Task.Factory.StartNew(() => { throw new Exception(message); });
}
2つの例外のうち1つだけがスローされますRun
。
スタックオーバーフローに関する他の質問はこの特定の問題に対処していないことに注意してください。重複を提案するときは注意してください。
私はあなたの質問の題名にその意味があることに同意しません。await
の行動は望ましくない。大多数のシナリオでは意味があります。でWhenAll
状況、どのくらいの頻度であなたは本当に知っておく必要がありますすべて1つだけではなく、エラーの詳細について
主な難しさAggregateException
例外処理です。つまり、特定の型を捕まえることができなくなります。
とは言っても、拡張メソッドを使用すると、希望する動作を簡単に取得できます。
public static async Task WithAggregateException(this Task source)
{
try
{
await source.ConfigureAwait(false);
}
catch
{
throw source.Exception;
}
}
UnobservedTaskException
(アプリケーションハンドラ)の代わりに繰り返されるtry
/catch
ブロック - Stephen ClearyUnobservedTaskException
まだ育っている監視されていない例外の場合(プロセスをクラッシュさせることはもうありません)。観察された、飲み込まれた例外がある唯一のケースはWhenAll
そして、それはあなたが複数の例外を持っているときにだけ起こりますではない飲み込んだ。あなたは本当にする必要がありますやってみるで例外を無視するawait
。 - Stephen ClearyAggregateException
少なくとも。お気軽にあなたのアイデアを話し合いのために投稿してください。余談:技術的には、await
展開しないでくださいAggregateException
しかし、これは問題ありません。 - Stephen Cleary
私は遅れていることを知っていますが、私はあなたが望むことをするこのきちんとした小さなトリックを見つけました。待機中のタスクでは例外のフルセットが利用可能であるため、このタスクの待機または.Resultを呼び出すと、集計例外がスローされます。
static void Main(string[] args)
{
var task = Run();
task.Wait();
}
public static async Task Run()
{
Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
var compositeTask = Task.WhenAll(tasks);
try
{
await compositeTask.ContinueWith((antecedant) => { }, TaskContinuationOptions.ExecuteSynchronously);
compositeTask.Wait();
}
catch (AggregateException aex)
{
foreach (var ex in aex.InnerExceptions)
{
Console.WriteLine(ex.Message);
}
}
}
static Task CreateTask(string message)
{
return Task.Factory.StartNew(() => { throw new Exception(message); });
}
私はもっと言うことができますが、それはただパディングになるでしょう。それと遊ぶ、それは彼らが言うように動作します。あなたはただ気をつけなければなりません。
多分あなたはこれが欲しい
(個人的には待たずに恥ずかしがり屋ですが、それが私の好みです
コメントに応えて(コメント返信には長すぎる)
それからここでのためのものの源があるベストプラクティスとして類似した議論のためのあなたの出発点としてスレッドを使用してください。
渡すためのコードを実装しない限り、例外は喜んで飲み込まれます(例えば、待機がラップしていると思われる非同期パターン...イベントを発生させるときにそれらをevent argsオブジェクトに追加する)。任意の数のスレッドを起動して実行するシナリオでは、順序や各スレッドを終了する時点を制御することはできません。さらに、あるパターンのエラーが別のパターンに関連している場合は、このパターンを使用しないでください。そのため、残りの部分の実行は完全に独立していることを強く示唆しています。強くこれらのスレッドの例外はすでに例外として扱われていることを意味します。これらのスレッドで発生したスレッドで例外を処理すること(奇妙なこと)を超えて何かをしたい場合は、参照によって渡されるロックコレクションにそれらを追加する必要があります。情報のコンカレントバッグを使用して、コンテキストを識別するために必要な情報に例外をラップします。
ユースケースを混同しないでください。
await
。 2番目のものは有効な答えを含んでいます。ありがとう。私は私が発見したいので私は数日の間他の答えを入らせますベスト溶液。 - usr
期待した例外だけを捉えるために練習をあきらめたくはありません。これにより、次のような拡張方法が得られます。
public static async Task NoSwallow<TException>(this Task task) where TException : Exception {
try {
await task;
} catch (TException) {
var unexpectedEx = task.Exception
.Flatten()
.InnerExceptions
.FirstOrDefault(ex => !(ex is TException));
if (unexpectedEx != null) {
throw new NotImplementedException(null, unexpectedEx);
} else {
throw task.Exception;
}
}
}
消費するコードは次のようになります。
try {
await Task.WhenAll(tasks).NoSwallow<MyException>();
catch (AggregateException ex) {
HandleExceptions(ex);
}
骨頭の例外は、例外が同時に発生した場合でも、同期の世界と同じ効果があります。MyException
偶然に。とのラッピングNotIplementedException
元のスタックトレースを失わないようにするのに役立ちます。
await
彼らがしたように?blogs.msdn.com/b/pfxteam/archive/2011/09/28/10217876.aspx - Matthew Watson