TPL继续任务:不确定谁是父任务

本文关键字:任务 继续 TPL 不确定 | 更新日期: 2023-09-27 18:22:22

ContinuationOption.AttachedToParent标志有问题。

这是我的伪代码:

   Task parentTask = Task.Start(() =>   
   { 
        Task childTask = Task.Start(() => doSomething(),
                                    ContinuationOption.AttachedToParent);
        childTask.ContinueWith(() => followingMethod(),  
                                    ContinuationOption.AttachedToParent);
   }

我知道如果"doSomething()"抛出异常childTask失败,parentTask也失败,因为ContinuationOption.AttachedToParent选项。

如果followingMethod()抛出异常,但parentTask状态为Completed,我会期望同样的行为。

是我做错了,还是继续任务的"父"任务不是我的"父任务"?

TPL继续任务:不确定谁是父任务

你的直觉是正确的,但我猜你很可能只是使用了错误的API。

我知道这只是伪代码,但Task.Start()是一个实例方法,而不是静态方法,所以它不会按照您所指示的方式编译,而且我们不知道您实际上是如何开始任务的,细节确实很重要。TaskFactory.StartNew会做你想做的事,但Task.Run不会。试试这个:

Task parent = Task.Factory.StartNew(() =>
{
  Task child = Task.Factory.StartNew(
     () => { Console.WriteLine("foo"); },
     TaskCreationOptions.AttachedToParent);
  Task continuation = child.ContinueWith(
     (Task prev) => { throw new InvalidOperationException("Test"); },
     TaskContinuationOptions.AttachedToParent);
});
try
{
  parent.Wait();
}
catch (AggregateException ex)
{
  if (ex.Flatten().InnerException is InvalidOperationException)
  { 
    Console.WriteLine("The continuation exception was propagated to parent");
  }
}

如果您更改

Task parent = Task.Factory.StartNew(...)

Task parent = Task.Run(...)

那么您将无法获得所需的行为,因为Task.Run本质上是TaskFactory.StartNew的包装器,它(除其他外)指定了TaskCreationOptions.DenyChildAttach,因此两个嵌套任务都将分离运行(没有父任务)。我猜这就是你的问题所在。

此上下文中的父任务是您在中创建子任务的任务,就像在Task.Factory.StartNew中的Task.Factory.StartNew中一样。

子任务(或嵌套任务)是在另一个任务(称为父任务)的用户委托中创建的System.Threading.Tasks.task实例。子任务可以是分离的,也可以是附加的。分离的子任务是独立于其父任务执行的任务。附加的子任务是使用TaskCreationOptions.AttachedToParent选项创建的嵌套任务。

附加和分离的子任务

注册任务的延续时,该任务是先行任务,而不是父任务。当您调用ContinueWith时,您将返回一个代表继续的任务。这是您需要检查异常的任务:

var continuation = task.ContinueWith(() => followingMethod());