为什么当父任务取消时,子任务的任务继续功能未被触发?

3

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

子任务的继续未能触发,即控制台未打印出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();

第一个继续项设置为已取消状态(因为其TaskContinuationOptions值导致其不运行),但令牌未被设置为已取消状态。因此,最后一个继续项在此处运行。

2个回答

3

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

您可能需要检查这里这里的取消文档。


4
你可能想避免你的猫在你发布答案时走过你的键盘。 - Lasse V. Karlsen

1
现在我明白了。您任务和嵌套任务中的委托会立即启动。嵌套任务开始等待2000毫秒,但它已经超过了检查取消令牌的点。嵌套任务中的2000毫秒等待导致继续被取消,因为同时取消了取消令牌。然而,父任务的继续没有取消令牌,因此它始终运行。
编辑:关键是两个任务都使用相同的取消令牌。

我在文档中错过了“[...]如果前置任务被取消,则不会启动后续任务。”。我对TaskContinuationOptions.OnlyOnCanceled选项有些困惑,似乎无法想象出前置任务被取消的情况下,后续任务仍然运行(且前置任务处于取消状态)。 - Marcus
你是指功能上的吗?技术上来说,你几乎已经自己创造了一个场景。试试看如果在创建令牌之后立即取消它会发生什么。 - Gert Arnold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接