嵌套任务和子任务

嵌套任务就是在另一个任务的用户委托中创建的 Task 实例。 子任务是使用 AttachedToParent 选项创建的嵌套任务。 一个任务可以创建任意数量的子任务和/或嵌套任务,该数量仅受系统资源限制。 下面的示例演示一个父任务,该父任务创建一个简单的嵌套任务。

Shared Sub SimpleNestedTask()
    Dim parent = Task.Factory.StartNew(Sub()
                                           Console.WriteLine("Outer task executing.")
                                           Dim child = Task.Factory.StartNew(Sub()
                                                                                 Console.WriteLine("Nested task starting.")
                                                                                 Thread.SpinWait(500000)
                                                                                 Console.WriteLine("Nested task completing.")
                                                                             End Sub)
                                       End Sub)
    parent.Wait()
    Console.WriteLine("Outer task has completed.")


End Sub

' Sample output:
'   Outer task executing.
'   Nested task starting.
'   Outer task has completed.
'   Nested task completing.
static void SimpleNestedTask()
{
    var parent = Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Outer task executing.");

        var child = Task.Factory.StartNew(() =>
        {
            Console.WriteLine("Nested task starting.");
            Thread.SpinWait(500000);
            Console.WriteLine("Nested task completing.");
        });
    });

    parent.Wait();
    Console.WriteLine("Outer has completed.");
}

/* Sample output:
        Outer task executing.
        Nested task starting.
        Outer has completed.
        Nested task completing.
 */

附加的子任务与分离的嵌套任务

子任务与嵌套任务之间最主要的区别是: 嵌套任务实质上独立于父任务或外部任务,而附加的子任务与父任务密切同步。 如果将任务创建语句改为使用 AttachedToParent 选项(如下面的示例所示),

Dim child = Task.Factory.StartNew(Sub()
                                      Console.WriteLine("Attached child starting.")
                                      Thread.SpinWait(5000000)
                                      Console.WriteLine("Attached child completing.")
                                  End Sub, TaskCreationOptions.AttachedToParent)
var child = Task.Factory.StartNew((t) =>
{

    Console.WriteLine("Attached child starting.");
    Thread.SpinWait(5000000);
    Console.WriteLine("Attached child completing.");
},                TaskCreationOptions.AttachedToParent);

将生成以下输出。

' Parent task executing.
' Attached child starting.
' Attached child completing.
' Parent has completed.
Parent task executing.
Attached child starting.
Attached child completing.
Parent has completed.

您可以使用附加的子任务创建异步操作的紧密同步关系图。 但是,在大多数情况下,因为与其他任务的关系不太复杂,所以建议使用嵌套任务。 这就是默认情况下嵌套其他任务内创建的任务的原因,您必须显式指定 AttachedToParent 选项以创建子任务。

下表列出了两种子任务的基本区别。

类别

嵌套任务

附加的子任务

外部任务(父任务)等待内部任务完成。

父任务传播子任务(内部任务)引发的异常。

父任务(外部任务)的状态依赖于子任务(内部的任务)的状态。

在嵌套任务为 Task<TResult> 的分离方案中,您仍可以通过访问嵌套任务的 Result 属性来强制父任务等待子任务。 Result 属性处于阻止状态,直到其任务完成。

Shared Sub WaitForSimpleNestedTask()
    Dim parent = Task(Of Integer).Factory.StartNew(Function()
                                                       Console.WriteLine("Outer task executing.")
                                                       Dim child = Task(Of Integer).Factory.StartNew(Function()
                                                                                                         Console.WriteLine("Nested task starting.")
                                                                                                         Thread.SpinWait(5000000)
                                                                                                         Console.WriteLine("Nested task completing.")
                                                                                                         Return 42
                                                                                                     End Function)
                                                       Return child.Result


                                                   End Function)
    Console.WriteLine("Outer has returned {0}", parent.Result)
End Sub
'Sample output:
' Outer task executing.
' Nested task starting.
' Detached task completing.
' Outer has returned 42
static void WaitForSimpleNestedTask()
{
    var outer = Task<int>.Factory.StartNew(() =>
    {
        Console.WriteLine("Outer task executing.");

        var nested = Task<int>.Factory.StartNew(() =>
        {
            Console.WriteLine("Nested task starting.");
            Thread.SpinWait(5000000);
            Console.WriteLine("Nested task completing.");
            return 42;
        });

        // Parent will wait for this detached child.
        return nested.Result;
    });

    Console.WriteLine("Outer has returned {0}.", outer.Result);
}

/* Sample output:
    Outer task executing.
    Nested task starting.
    Nested task completing.
    Outer has returned 42.
 */

嵌套任务和子任务中的异常

如果嵌套任务引发异常,必须像任何非嵌套任务一样,在外部任务中直接查看或处理该异常。 如果附加的子任务引发异常,该异常会自动传播到父任务并传回等待或尝试访问该任务的 Result 属性的线程。 因此,通过使用附加的子任务,您仅在一个位置点就可以处理所有异常,即在调用线程上调用 Wait。 有关更多信息,请参见异常处理(任务并行库)

取消和子任务

请记住,任务取消是协作性的操作。 因此,若要“可取消”,每个附加或分离的子任务必须监视取消标记的状态。 如果要通过使用一个取消请求来取消父任务及其所有子任务,请将同一标记作为参数传递给所有任务,并在每个任务中提供逻辑以响应请求。 有关更多信息,请参见任务取消如何:取消任务及其子级

父任务何时取消

如果父任务在子任务启动之前取消自己,则子任务(嵌套任务)将显然永远不会启动。 如果父任务在子任务或嵌套任务启动后取消自己,则嵌套任务(子任务)将运行直到完成,除非它具有自己的取消逻辑。 有关更多信息,请参见任务取消

嵌套任务何时取消

如果分离的子任务通过使用传递给它的同一标记取消自己,且父任务不等待子任务,则不会传播异常,因为异常被视为良性协作取消。 此行为与任何顶级任务的行为相同。

子任务何时取消

当附加的子任务通过使用传递给它的同一标记取消自己时,TaskCanceledException 传播到 AggregateException 内的联接线程。 等待父任务非常重要,这样,除了可以处理通过附加子任务的关系图向上传播的所有出错异常,还可以处理向上传播的所有良性异常。

有关更多信息,请参见异常处理(任务并行库)

请参见

概念

.NET Framework 中的并行编程

数据并行(任务并行库)