web-dev-qa-db-ja.com

条件付き継続のあるタスクの使用

条件付き継続でタスクを使用する方法について少し混乱しています。

タスクがある場合、成功とエラーを処理するタスクを続行し、それらが完了するのを待ちます。

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を呼び出すとスローされるためです。 「タスクがキャンセルされました」という例外を取得せずにこれらに参加するにはどうすればよいですか?

31
dan

あなたの主な問題は、あなたがこれらの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();
    }
}
_
11
Bryan Ray

それは正常ではありませんか?

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を完了するのと同じことが得られます。これは、タスクから返されるすべての例外のコレクションです。

0
Khepri

Task.WaitAny(onSuccess, onError);を使用する

0
Dejisys