2

Taskたとえば、タイムアウトが発生した後にキャンセルされる可能性のある s の継続チェーンがあります。キャンセルが発生したときに実行されていたタスクを特定したいと考えています。これが私が話していることです:

CancellationTokenSource cts = new CancellationTokenSource();

Task timeoutTask = Task.Delay(5000).ContinueWith((t) => cts.Cancel());

Task workChain = Task.Factory.StartNew((t) =>
{
    Console.WriteLine("Running task " + Task.CurrentId);
    Thread.Sleep(1000);
}, -1, cts.Token);

Task parent = workChain;
for (int i = 0; i < 10; i++)
{
    parent = parent.ContinueWith((t, o) =>
        {
            Console.WriteLine("Running task " + Task.CurrentId);
            Console.WriteLine("Last Task.AsyncState = " + t.AsyncState);
            Thread.Sleep(1000);
        }, i, cts.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current);
}
parent.ContinueWith((t) =>
{
    Console.WriteLine("Cancel task " + Task.CurrentId);
    Console.WriteLine("Last running Task.AsyncState = " + t.AsyncState);
}, TaskContinuationOptions.OnlyOnCanceled);

上記を実行すると、渡された前提条件は、OnlyOnCanceledキャンセルされたときに実行されていたタスクではありません。理由はわかっています。OnlyOnCanceledタスク、ループで作成された最後のタスクによって親にされ、タイムアウトが発生すると、完了していないすべてのタスクが開始されずにキャンセルされたものとしてマークされます。

各タスクでトークンの状態を確認し、何かを別の場所に保存することは、ほとんどの場合うまくいきますが、1 つのタスクが完了した後、次のタスクが開始される前にキャンセルが発生する可能性がわずかにあります。その場合、最初にキャンセルされたタスクについては何もわかりません。タスクが開始されたときに何かを保存し、キャンセルされたときに何かを保存することはいつでもできますが、これはすぐにぎこちなく感じ始めます。

4

2 に答える 2

0

私が見つけたものは私にとってはうまくいきますが、それでも必要なほどきれいではありません:

  • メイン チェーンの各タスクは を取りませんが、実行さCancellationTokenれる 2 番目の継続 (メイン チェーンの一部ではない) を取得しますOnlyOnFaulted
  • これらの各タスクは、子タスクで作業を行い、 を呼び出して子タスクを待機します.Wait(CancellationToken)。トークンが取り消されると、Wait 呼び出しは例外をスローし、実行中のタスクに障害が発生します。これにより、子タスクの完了を待たずにエラーを報告できます。
  • メイン チェーンのすべてのタスクが実行されますOnlyOnRanToCompletion
  • 実行されるメイン チェーンの最後にある 1 つのタスクは、OnlyOnRanToCompletionチェーン全体の成功を報告します (つまり、タイムアウトなし)。

トークンがキャンセルされると、現在実行中のタスクは子タスクの待機を停止し、OperationCanceledException. OperationCanceledExceptionそのタスクのサイド ブランチは、前件の の存在を特別にケース化することによって、例外 (またはその他の例外) を処理しますAggregateException.InnerExceptions。メイン チェーンのタスクに障害が発生したため、メイン チェーンの他のタスクは実行されません。

CancellationTokenSource cts = new CancellationTokenSource(25000);

Task workChain = Task.Factory.StartNew((o) =>
{
    Console.WriteLine("Running task {0} with state {1}", Task.CurrentId, o);
    Thread.Sleep(1000);
}, -1);

Task parent = workChain;
for (int i = 0; i < 10; i++)
{
    parent = parent.ContinueWith((ante, o) =>
        {
            Console.WriteLine("Running task {0} with state {1}", Task.CurrentId, o);
            Task subTask = Task.Factory.StartNew(() =>
            {
                Thread.Sleep(10000);
                Console.WriteLine("Subtask completed");
            });
            subTask.Wait(cts.Token);
        }, i, TaskContinuationOptions.OnlyOnRanToCompletion);
    parent.ContinueWith((ante) =>
        {
            foreach (Exception e in ante.Exception.InnerExceptions)
            {
                if (e is OperationCanceledException)
                {
                    //report timeout
                    Console.WriteLine("Timed out while running task id {0}", ante.Id);
                    return;
                }
            }
            //report other exception
            Console.WriteLine("Something bad happened: {0}", ante.Exception.GetBaseException());
        }, TaskContinuationOptions.OnlyOnFaulted);
}
Task lastTask = parent.ContinueWith((ante) =>
    {
        //report success
        Console.WriteLine("Success");
    }, TaskContinuationOptions.OnlyOnRanToCompletion);
于 2013-10-11T15:04:16.070 に答える