为什么在取消父级时未触发子任务的任务延续?

时间:2011-08-17 13:27:57

标签: c# .net task-parallel-library

考虑以下C#代码:

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();

子任务的继续不会触发,即B不会打印到控制台。注释掉取消的调用会打印B。

观察到的:

A
C

预期

A
B
C

如果父项被取消,似乎不会触发子任务的继续,但我找不到这个记录。任何人都知道为什么/这里发生了什么,或者记录在哪里?

编辑:

我忘记将取消令牌传递给最后的续集。实际上,我发现文档是读的 “[...]如果先前被取消,继续将不会开始”,但是我对选项TaskContinuation.OnlyOnCanceled感到困惑。这种情况是可能的,例如

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();

第一个continuation设置为state cancelled(因为它的TaskContinuationOptions值导致它不能运行),而没有将令牌设置为取消。所以最后一个延续确实在这里运行。

2 个答案:

答案 0 :(得分:3)

最终,如果子任务附加到其父级,则它依赖于它,并且由于您使用相同的取消令牌,因此可以预期此行为。您可以使用单独的取消令牌,但我认为您需要处理冒泡取消异常以保持流量 - 另一个选择是分离任务。

您可能需要查看herehere以获取有关取消的文档。

答案 1 :(得分:1)

现在我知道了。您的任务和嵌套任务中的代表立即开始。嵌套任务开始等待2000毫秒,但它已经超出了检查取消令牌的程度。嵌套任务中的2000毫秒等待导致延续取消,因为同时取消令牌被取消。但是,父任务的继续没有取消令牌,因此它始终运行。

编辑:重点是两个任务都使用相同的取消令牌。