条件付き継続でタスクを使用する方法について少し混乱しています。
タスクがある場合、成功とエラーを処理するタスクを続行し、それらが完了するのを待ちます。
void FunctionThrows() {throw new Exception("faulted");}
static void MyTest()
{
var taskThrows = Task.Factory.StartNew(() => FunctionThrows());
var onSuccess = taskThrows.ContinueWith(
prev => Console.WriteLine("success"),
TaskContinuationOptions.OnlyOnRanToCompleted);
var onError = taskThrows.ContinueWith(
prev => Console.WriteLine(prev.Exception),
TaskContinuationOptions.OnlyOnFaulted);
//so far, so good
//this throws because onSuccess was cancelled before it was started
Task.WaitAll(onSuccess, onError);
}
これは、タスクの成功/失敗の分岐を行うための好ましい方法ですか?また、これらすべてのタスクにどのように参加することになっているのでしょうか。それぞれに独自のエラー処理がある長い継続行を作成したとします。
//for example
var task1 = Task.Factory.StartNew(() => ...)
var task1Error = task1.ContinueWith( //on faulted
var task2 = task1.ContinueWith( //on success
var task2Error = task2.ContinueWith( //on faulted
var task3 = task2.ContinueWith( //on success
//etc
これらのスローでWaitAll
を呼び出すと、継続の一部がTaskContinuationOptions
のためにキャンセルされ、キャンセルされたタスクでWait
を呼び出すとスローされるためです。 「タスクがキャンセルされました」という例外を取得せずにこれらに参加するにはどうすればよいですか?
あなたの主な問題は、あなたがこれらの2つのタスクに「待つ」ように言っていることだと思います。
_Task.WaitAll(onSuccess, onError);
_
onSuccessおよびonError継続は自動的にセットアップされ、先行タスクの後に実行されます完了します。
Task.WaitAll(...)
をtaskThrows.Start();
に置き換えるだけで、目的の出力が得られると思います。
これが私がまとめた例のビットです:
_class Program
{
static int DivideBy(int divisor)
{
Thread.Sleep(2000);
return 10 / divisor;
}
static void Main(string[] args)
{
const int value = 0;
var exceptionTask = new Task<int>(() => DivideBy(value));
exceptionTask.ContinueWith(result => Console.WriteLine("Faulted ..."), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
exceptionTask.ContinueWith(result => Console.WriteLine("Success ..."), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
exceptionTask.Start();
try
{
exceptionTask.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine("Exception: {0}", ex.InnerException.Message);
}
Console.WriteLine("Press <Enter> to continue ...");
Console.ReadLine();
}
}
_
それは正常ではありませんか?
MSDNのドキュメントを見ると、問題なく実行されており、実装しているロジックは適切です。足りないのは、WaitAll呼び出しを次のようにAggregateExceptionラッパーでラップすることだけです。
// Exceptions thrown by tasks will be propagated to the main thread
// while it waits for the tasks. The actual exceptions will be wrapped in AggregateException.
try
{
// Wait for all the tasks to finish.
Task.WaitAll(tasks);
// We should never get to this point
Console.WriteLine("WaitAll() has not thrown exceptions. THIS WAS NOT EXPECTED.");
}
catch (AggregateException e)
{
Console.WriteLine("\nThe following exceptions have been thrown by WaitAll(): (THIS WAS EXPECTED)");
for (int j = 0; j < e.InnerExceptions.Count; j++)
{
Console.WriteLine("\n-------------------------------------------------\n{0}", e.InnerExceptions[j].ToString());
}
}
詳細については、こちらをご覧ください: http://msdn.Microsoft.com/en-us/library/dd270695.aspx
本質的に、AggregatedExceptionをキャッチすると、WaitAllを完了するのと同じことが得られます。これは、タスクから返されるすべての例外のコレクションです。
Task.WaitAny(onSuccess, onError);
を使用する