プロパティAggregateException
を検査していないため、例外が発生しますか?Exception
いいえ、g
TPL によってタスクがキャンセルされるため、例外が発生します (msdn が述べているように、先行タスクが例外をスローした場合、このタスクはスケジュールされないため)。
ここには 3 つのタスクがあります。
- 元のタスク (StartNew を使用する)
- 最初の継続タスク (例外をスローする)
- 2 番目の継続タスク (OK を出力) (これはコードの g タスクです)。
問題は、 2 番目のタスクが正常に終了した場合にのみ、 TPL に 3 次元タスクを開始するように依頼することです。つまり、この条件が満たされない場合、TPL は新しく作成されたタスクを完全にキャンセルします。
監視しない一時的なタスク (私のリストのタスク 2) があるため、監視されていないタスクの例外が発生しました。フォルト状態を観察することはないため、ファイナライザーをスローしてそれについて通知します。
これは、catch ブロックでタスクのステータスを出力することで確認できます。
catch (AggregateException ex)
{
Console.WriteLine("catch");
// Will print: Status in catch: Canceled
Console.WriteLine("Status in catch: {0}", g.Status);
}
前件が (各行で) 例外をスローするかどうかを常に検査する必要がありますか? (一行一行確認できない!意味が分からなくて面倒くさい)
はい、この問題を回避するには、先行タスクの例外を観察する必要があります。
static class TaskEx
{
public static Task ObserverExceptions(this Task task)
{
task.ContinueWith(t => { var ignore = t.Exception; },
TaskContinuationOptions.OnlyOnFaulted);
return task;
}
}
そして、次のように使用します。
var g= Task.Factory.StartNew<int> (() => 8)
.ContinueWith (ant =>{throw null;})
.ObserveExceptions()
.ContinueWith (a =>{ Console.WriteLine("OK");});
try{
Console.WriteLine("1");
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{Console.WriteLine("catch"); }
更新: 最後の箇条書きに解決策を追加
try catch ブロックは例外を飲み込むべきではありませんでしたか? (私はすべての例外がwaitメソッドにバブルアップすると思っていました....そうですか?)
TransformWith
私たちのプロジェクトには、この特定の問題を解決して以下を得ることができる一連の拡張メソッド ( と呼ばれる) があります。
- 例外は catch ブロックにバブルアップし、
- アプリケーションをクラッシュさせません
TaskUnobservedException
使い方はこちら
var g = Task.Factory.StartNew(() => 8)
.ContinueWith(ant => { throw null; })
// Using our extension method instead of simple ContinueWith
.TransformWith(t => Console.WriteLine("OK"));
try
{
Console.WriteLine("1");
// Will fail with NullReferenceException (inside AggregateExcpetion)
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{
// ex.InnerException is a NullReferenceException
Console.WriteLine(ex.InnerException);
}
そして、ここに拡張メソッドがあります:
static class TaskEx
{
public static Task TransformWith(this Task future, Action<Task> continuation)
{
var tcs = new TaskCompletionSource<object>();
future
.ContinueWith(t =>
{
if (t.IsCanceled)
{
tcs.SetCanceled();
}
else if (t.IsFaulted)
{
tcs.SetException(t.Exception.InnerExceptions);
}
else
{
try
{
continuation(future);
tcs.SetResult(null);
}
catch (Exception e)
{
tcs.SetException(e);
}
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
}