Tareas anidadas y tareas secundarias

Una tarea anidada no es más que una instancia de Task que se crea en el delegado de usuario de otra tarea. Una tarea secundaria es una tarea anidada que se crea con la opción AttachedToParent. Una tarea puede crear cualquier número de tareas secundarias y anidadas, con la única limitación de los recursos del sistema. En el ejemplo siguiente se muestra una tarea primaria que crea una tarea anidada simple.

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.
 */

Tareas secundarias asociadas frente a tareas anidadas desasociadas

El aspecto más importante en lo que se refiere a las tareas secundarias y anidadas es que las tareas anidadas son esencialmente independientes de la tarea primaria o externa, mientras que las tareas secundarias asociadas están estrechamente sincronizadas con el la tarea primaria. Si se modifica la instrucción de creación de la tarea para usar la opción AttachedToParent, como se muestra en el siguiente ejemplo,

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

se generará el siguiente resultado.

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

Puede usar tareas secundarias asociadas para crear gráficos de operaciones asincrónicas con una estrecha sincronización. Sin embargo, en la mayoría de los escenarios, recomendamos usar tareas anidadas porque las relaciones con otras tareas son menos complejas. Esta es la razón por la que las tareas que se crean dentro de otras tareas están anidadas de forma predeterminada y es necesario especificar explícitamente la opción AttachedToParent para crear una tarea secundaria.

En la tabla siguiente se muestran las diferencias básicas entre los dos tipos de tareas secundarias.

Categoría

Tareas anidadas

Tareas secundarias asociadas

La tarea externa (primaria) espera a que las tareas internas se completen.

No

La tarea primaria propaga las excepciones iniciadas por las tareas secundarias (tareas internas).

No

El estado de la tarea primaria (tarea externa) depende del estado de la tarea secundaria (tarea interna).

No

En escenarios desasociados en los que la tarea anidada es un objetoTask<TResult>, se puede forzar que la tarea primaria espere a la secundaria mediante el acceso a la propiedad Result de la tarea anidada. La propiedad Result se bloquea hasta que su tarea se completa.

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.
 */

Excepciones en tareas anidadas y secundarias

Si una tarea anidada produce una excepción, debe observarse o controlarse directamente en la tarea exterior como si se tratara de una tarea no anidada. Si una tarea secundaria adjunta inicia una excepción, la excepción se propaga automáticamente a la tarea primaria y de nuevo al subproceso, que espera o intenta obtener acceso a la propiedad Result de la tarea. Por tanto, si se usan tareas secundarias asociadas, se pueden controlar todas las excepciones en un solo punto: la llamada a Wait del subproceso que realiza la llamada. Para obtener más información, vea Control de excepciones (Task Parallel Library).

Cancelación y tareas secundarias

No conviene olvidar que la cancelación de tareas es cooperativa. Por tanto, para ser "cancelable", cada tarea secundaria asociada o desasociada debe supervisar el estado del token de cancelación. Si desea cancelar un elemento primario y todos sus elementos secundarios utilizando una sola solicitud de cancelación, debe pasar el mismo token como argumento a todas las tareas y proporcionar en cada tarea la lógica de respuesta a la solicitud. Para obtener más información, vea Cancelación de tareas y Cómo: Cancelar una tarea y sus elementos secundarios.

Cuando la tarea primaria se cancela

Si una tarea primaria se cancela antes de que se inicie una tarea secundaria, como es lógico, la tarea secundaria (anidada) nunca se cancelará. Si una tarea primaria se cancela después de que se ha iniciado una tarea secundaria o anidada, la tarea anidada (secundaria) se ejecutará hasta completarse a menos que tenga su propia lógica de cancelación. Para obtener más información, vea Cancelación de tareas.

Cuando una tarea anidada se cancela

Si una tarea secundaria desasociada se cancela usando el mismo token que se pasó a la tarea y la tarea primaria no espera a la secundaria, no se propagará ninguna excepción, pues la excepción se trata cono una cancelación de cooperación benigna. Este comportamiento es igual que el de cualquier tarea de nivel superior.

Cuando una tarea secundaria se cancela

Cuando una tarea secundaria asociada se cancela usando el mismo token que se pasó a la tarea, se propaga una excepción TaskCanceledException al subproceso de unión dentro de AggregateException. Es muy importante esperar a la tarea primaria para poder controlar todas las excepciones benignas además de todos las excepciones de error que se propagan de manera ascendente a través de un gráfico de tareas secundarias asociadas.

Para obtener más información, vea Control de excepciones (Task Parallel Library).

Vea también

Conceptos

Programación paralela en .NET Framework

Paralelismo de datos (Task Parallel Library)