开发者

Why is a task continuation for a child task not triggered when parent is cancelled?

Consider the following C# code:

CancellationTokenSource tCancelSource = new CancellationTokenSource();
var tTask = Task.Factory.StartNew
(
   () =>
   {
// Start child
      Task.Factory.StartNew
        (() =>
           {
               Thread.Sleep(2000);
               Console.WriteLine("A");
               tCancelSource.Token.ThrowIfCancellationRequested();
            }
         , tCancelSource.Token
         , TaskCreationOptions.AttachedToParent
         , TaskScheduler.Current
        )
      .ContinueWith
          (
            t =>
              {
                  Console.WriteLine("B");
              }
            , tCancelSource.Token, TaskContinuationOptions.AttachedToParent
            , TaskScheduler.Current
          );
   }
   , tCancelSource.Token, TaskCreationOptions.PreferFairness
   , TaskScheduler.Current
)
.ContinueWith
      (t => Console.WriteLine("C"));
Thread.Sleep(500);
tCancelSource.Cancel();
tTask.Wait();
Console.WriteLine("Done"); Console.Read();

The continuation of the child task does not fire, i.e. B is not printed to the Console. Commenting out the call to Cancel would print B.

Observed:

A
C

Expected

A
B
C

It seems that the continuation of a child task is not fired if the parent is cancelled, but I can't find this documented. Anyone have any idea why/what's going on here, or where this would be documented?

Edit:

I forgot to pass the cancellation token to the final continuation. Indeed, I found the documentation reads "[...]If the antecedent was canceled, the continuation will not be started", however I got confused b开发者_开发问答y the option TaskContinuation.OnlyOnCanceled. Such a scenario is possible, for example

CancellationTokenSource tSource = new CancellationTokenSource();
Task tTest = Task.Factory.StartNew
        (
            () =>
            {
               throw new Exception("foobar");
            }, tSource.Token).ContinueWith(t =>
                 {
                    Console.WriteLine("A " + t.Status);
                  }
             , tSource.Token
             , TaskContinuationOptions.NotOnFaulted
             , TaskScheduler.Current
          )
          .ContinueWith
              (
                 t =>
                   {
                      Console.WriteLine("B " + t.Status);
                    }
                 , tSource.Token
               );

tTest.Wait();
Console.Read();

the first continuation is set to state cancelled (as its TaskContinuationOptions value causes it not to run), without the token being set to cancelled. So the last continuation does run here.


Ultimately, if the child task is attached to its parent then it is dependent on it, and since you're using the same cancellation token you can expect this behaviour. You could use a separate cancellation token, but then I think you'll need to handle the bubbling cancellation exception in order to maintain flow - another option is to detach the task.

You might want to check here and here for documentation on cancellation.


Now I know. The delegates in your task and nested task start right away. The nested task starts waiting 2000 ms, but it is already beyond the point where it checks the cancellation token. The 2000 ms wait in the nested task causes the continuation to cancel, because in the mean time the cancel token is cancelled. However, the continuation of the parent task has no cancellation token, so it always runs.

Edit: the point is that both tasks use the same cancellation token.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜