Partager via


Tâches imbriquées et tâches enfants

Une tâche imbriquée est une instance Task créée dans le délégué utilisateur d'une autre tâche. Une tâche enfant est une tâche imbriquée créée avec l'option AttachedToParent. Une tâche peut créer un nombre d'enfants et/ou de tâches imbriquées, limité uniquement par les ressources système. L'exemple suivant affiche une tâche parent qui crée une tâche imbriquée 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.
 */

Tâches enfants attachées contre tâches imbriquées détachées

Le principale différence entre les enfants et les tâches imbriquées est que les tâches imbriquées sont essentiellement indépendantes de la tâche parent ou externe, alors que les tâches enfants attachées sont étroitement synchronisées avec le parent. Si vous modifiez l'instruction de création de tâche pour utiliser l'option AttachedToParent, comme indiqué dans l'exemple suivant,

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

la sortie suivante est produite.

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

Vous pouvez utiliser des tâches enfants attachées pour créer des graphiques étroitement synchronisés d'opérations asynchrones. Toutefois, dans la plupart des scénarios, il est recommandé d'utiliser des tâches imbriquées car les relations avec les autres tâches sont moins complexes. C'est pourquoi les tâches créées à l'intérieur d'autres tâches sont imbriquées par défaut et l'option AttachedToParent doit être spécifiée explicitement pour créer une tâche enfant.

Le tableau suivant répertorie les différences de base entre les deux genres de tâches enfants.

Catégorie

Tâches imbriquées

Tâches enfants attachées

La tâche externe (parent) attend que les tâches internes se terminent.

Non

Oui

Le parent propage des exceptions levées par les enfants (tâches internes).

Non

Oui

État du parent (tâche externe) dépendant de l'état de l'enfant (tâche interne).

Non

Oui

Dans les scénarios détachés où la tâche imbriquée est un Task<TResult>, vous pouvez encore forcer le parent à attendre un enfant en accédant à la propriété Result de la tâche imbriquée. La propriété Result se bloque jusqu'à ce que sa tâche se termine.

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

Exceptions dans les tâches imbriquées et enfants

Si une tâche imbriquée lève une exception, elle doit être observée ou gérée directement dans la tâche externe comme pour toute tâche non imbriquée. Si un enfant attaché lève une exception, l'exception est propagée automatiquement vers la tâche parent puis vers le thread qui attend ou essaie d'accéder à la propriété Result de la tâche. Par conséquent, en utilisant des tâches enfants attachées, vous pouvez gérer toutes les exceptions à un seul moment, à savoir l'appel pour attendre le thread appelant. Pour plus d'informations, consultez Gestion des exceptions (bibliothèque parallèle de tâches).

Annulation et tâches enfants

Souvenez-vous que l'annulation de tâche est coopérative. Par conséquent, pour être « annulable », chaque tâche enfant attachée ou détachée doit surveiller l'état du jeton d'annulation. Si vous voulez annuler un parent et tous ses enfants avec une requête d'annulation, passez un même jeton en tant qu'argument à toutes les tâches et fournissez dans chaque tâche la logique pour répondre à la requête. Pour plus d'informations, consultez Annulation de tâches et Comment : annuler une tâche et ses enfants.

Annulation d'un parent

Si un parent s'annule avant qu'un enfant ne soit démarré, la tâche enfant (imbriquée) ne démarrera évidemment pas. Si un parent s'annule après avoir démarré une tâche enfant ou imbriquée, la tâche imbriquée (enfant) terminera son exécution, à moins qu'elle ait sa propre logique d'annulation. Pour plus d'informations, consultez Annulation de tâches.

Annulation d'une tâche imbriquée

Si une tâche enfant détachée s'annule à l'aide du même jeton passé à la tâche, et si le parent n'attend pas l'enfant, aucune exception n'est propagée car l'exception est traitée comme annulation de coopération bénigne. Ce comportement est le même que celui de toute tâche de niveau supérieur.

Annulation d'une tâche enfant

Lorsqu'une tâche enfant attachée s'annule à l'aide du même jeton passé à la tâche, un TaskCanceledException est propagé au thread joint à l'intérieur d'un AggregateException. Il est très important d'attendre la tâche parent afin de pouvoir gérer toutes les exceptions bénignes, en plus de toute exception d'erreur propagée vers un graphique de tâches enfants attachées.

Pour plus d'informations, consultez Gestion des exceptions (bibliothèque parallèle de tâches).

Voir aussi

Concepts

Programmation parallèle dans le .NET Framework

Parallélisme de données (bibliothèque parallèle de tâches)