Tâches enfants attachées et détachéesAttached and Detached Child Tasks

Une tâche enfant (ou tâche imbriquée) est une instance System.Threading.Tasks.Task créée dans le délégué utilisateur d’une autre tâche, appelée tâche parent.A child task (or nested task) is a System.Threading.Tasks.Task instance that is created in the user delegate of another task, which is known as the parent task. Une tâche enfant peut être détachée ou attachée.A child task can be either detached or attached. Une tâche enfant détachée est une tâche qui s’exécute indépendamment de son parent.A detached child task is a task that executes independently of its parent. Une tâche enfant attachée est une tâche imbriquée créée avec l’option TaskCreationOptions.AttachedToParent dont le parent ne l’empêche pas explicitement ou par défaut d’être attachée.An attached child task is a nested task that is created with the TaskCreationOptions.AttachedToParent option whose parent does not explicitly or by default prohibit it from being attached. Une tâche peut créer autant de tâches enfants attachées et détachées que le permettent les ressources système.A task may create any number of attached and detached child tasks, limited only by system resources.

Le tableau suivant répertorie les principales différences entre les deux types de tâches enfants.The following table lists the basic differences between the two kinds of child tasks.

CategoryCategory Tâches enfants détachéesDetached child tasks Tâches enfants attachéesAttached child tasks
Le parent attend que les tâches enfants soient terminées.Parent waits for child tasks to complete. NonNo OuiYes
Le parent propage les exceptions levées par les tâches enfants.Parent propagates exceptions thrown by child tasks. NonNo OuiYes
Le statut du parent dépend du statut de l'enfant.Status of parent depends on status of child. NonNo OuiYes

Dans la plupart des scénarios, nous vous recommandons d’utiliser des tâches enfants détachées, car leurs relations avec les autres tâches sont moins complexes.In most scenarios, we recommend that you use detached child tasks, because their relationships with other tasks are less complex. C'est pourquoi les tâches créées à l'intérieur de tâches parentes sont détachées par défaut et vous devez spécifier explicitement l'option TaskCreationOptions.AttachedToParent pour créer une tâche enfant attachée.That is why tasks created inside parent tasks are detached by default, and you must explicitly specify the TaskCreationOptions.AttachedToParent option to create an attached child task.

Tâches enfants détachéesDetached child tasks

Même si une tâche enfant est créée par une tâche parente, par défaut, elle est indépendante de celle-ci.Although a child task is created by a parent task, by default it is independent of the parent task. Dans l'exemple suivant, une tâche parente crée une tâche enfant simple.In the following example, a parent task creates one simple child task. Si vous exécutez l'exemple de code plusieurs fois, vous pouvez remarquer que la sortie de l'exemple diffère de celle indiquée et éventuellement d'une exécution à l'autre.If you run the example code multiple times, you may notice that the output from the example differs from that shown, and also that the output may change each time you run the code. En effet, la tâche parente et les tâches enfants s'exécutent indépendamment les unes des autres ; l'enfant est une tâche détachée.This occurs because the parent task and child tasks execute independently of each other; the child is a detached task. L'exemple attend seulement que la tâche parente se termine, et la tâche enfant ne peut pas s'exécuter ou s'achever avant la fin de l'application console.The example waits only for the parent task to complete, and the child task may not execute or complete before the console app terminates.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      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.");
   }
}
// The example produces output like the following:
//        Outer task executing.
//        Nested task starting.
//        Outer has completed.
//        Nested task completing.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      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
End Module
' The example produces output like the following:
'   Outer task executing.
'   Nested task starting.
'   Outer task has completed.
'   Nested task completing.

Si la tâche enfant est représentée par un objet Task<TResult> plutôt que par un objet Task, vous pouvez vous assurer que la tâche parente attend la fin de la tâche enfant en accédant à la propriété Task<TResult>.Result de celle-ci, même s'il s'agit d'une tâche enfant détachée.If the child task is represented by a Task<TResult> object rather than a Task object, you can ensure that the parent task will wait for the child to complete by accessing the Task<TResult>.Result property of the child even if it is a detached child task. La propriété Result bloque jusqu’à ce que sa tâche se termine, comme le montre l’exemple suivant.The Result property blocks until its task completes, as the following example shows.

using System;
using System.Threading;
using System.Threading.Tasks;

class Example
{
   static void Main()
   {
      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);
   }
}
// The example displays the following output:
//       Outer task executing.
//       Nested task starting.
//       Nested task completing.
//       Outer has returned 42.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      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
End Module      
' The example displays the following output:
'       Outer task executing.
'       Nested task starting.
'       Detached task completing.
'       Outer has returned 42

Tâches enfants attachéesAttached child tasks

Contrairement aux tâches enfants détachées, les tâches enfants attachées sont étroitement synchronisées avec le parent.Unlike detached child tasks, attached child tasks are closely synchronized with the parent. Vous pouvez convertir la tâche enfant détachée dans l'exemple précédent en tâche enfant attachée à l'aide de l'option TaskCreationOptions.AttachedToParent dans l'instruction de création de tâche, comme illustré dans l'exemple suivant.You can change the detached child task in the previous example to an attached child task by using the TaskCreationOptions.AttachedToParent option in the task creation statement, as shown in the following example. Dans ce code, la tâche enfant attachée se termine avant son parent.In this code, the attached child task completes before its parent. La sortie de l'exemple est donc la même chaque fois que vous exécutez le code.As a result, the output from the example is the same each time you run the code.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
            Console.WriteLine("Parent task executing.");
            var child = Task.Factory.StartNew(() => {
                  Console.WriteLine("Attached child starting.");
                  Thread.SpinWait(5000000);
                  Console.WriteLine("Attached child completing.");
            }, TaskCreationOptions.AttachedToParent);
      });
      parent.Wait();
      Console.WriteLine("Parent has completed.");
   }
}
// The example displays the following output:
//       Parent task executing.
//       Attached child starting.
//       Attached child completing.
//       Parent has completed.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Factory.StartNew(Sub()
                                            Console.WriteLine("Parent task executing")
                                            Dim child = Task.Factory.StartNew(Sub()
                                                                                 Console.WriteLine("Attached child starting.")
                                                                                 Thread.SpinWait(5000000)
                                                                                 Console.WriteLine("Attached child completing.")
                                                                              End Sub, TaskCreationOptions.AttachedToParent)
                                         End Sub)
      parent.Wait()
      Console.WriteLine("Parent has completed.")
   End Sub
End Module
' The example displays the following output:
'       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.You can use attached child tasks to create tightly synchronized graphs of asynchronous operations.

Toutefois, une tâche enfant ne peut s’attacher à son parent que si celui-ci n’interdit pas les tâches enfants attachées.However, a child task can attach to its parent only if its parent does not prohibit attached child tasks. Pour empêcher explicitement l’attachement de tâches enfants à une tâche parente, vous devez spécifier l’option TaskCreationOptions.DenyChildAttach dans le constructeur de classe de la tâche parente ou la méthode TaskFactory.StartNew.Parent tasks can explicitly prevent child tasks from attaching to them by specifying the TaskCreationOptions.DenyChildAttach option in the parent task's class constructor or the TaskFactory.StartNew method. Pour une interdiction implicite, vous devez créer les tâches parentes en appelant la méthode Task.Run.Parent tasks implicitly prevent child tasks from attaching to them if they are created by calling the Task.Run method. L'exemple suivant illustre ce comportement.The following example illustrates this. Il est identique à l’exemple précédent, même si la tâche parente est créée en appelant la méthode Task.Run(Action) plutôt que la méthode TaskFactory.StartNew(Action).It is identical to the previous example, except that the parent task is created by calling the Task.Run(Action) method rather than the TaskFactory.StartNew(Action) method. Étant donné que la tâche enfant n’est pas en mesure de s’attacher à son parent, la sortie de l’exemple est imprévisible.Because the child task is not able to attach to its parent, the output from the example is unpredictable. Étant donné que les options de création de tâches par défaut pour les surcharges Task.Run incluent TaskCreationOptions.DenyChildAttach, cet exemple est fonctionnellement équivalent au premier exemple dans la section « Tâches enfants détachées ».Because the default task creation options for the Task.Run overloads include TaskCreationOptions.DenyChildAttach, this example is functionally equivalent to the first example in the "Detached child tasks" section.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var parent = Task.Run(() => {
            Console.WriteLine("Parent task executing.");
            var child = Task.Factory.StartNew(() => {
                  Console.WriteLine("Child starting.");
                  Thread.SpinWait(5000000);
                  Console.WriteLine("Child completing.");
            }, TaskCreationOptions.AttachedToParent);
      });
      parent.Wait();
      Console.WriteLine("Parent has completed.");
   }
}
// The example displays output like the following:
//       Parent task executing
//       Parent has completed.
//       Attached child starting.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Run(Sub()
                               Console.WriteLine("Parent task executing")
                               Dim child = Task.Factory.StartNew(Sub()
                                              Console.WriteLine("Attached child starting.")
                                              Thread.SpinWait(5000000)
                                              Console.WriteLine("Attached child completing.")
                                           End Sub, TaskCreationOptions.AttachedToParent)
                            End Sub)
      parent.Wait()
      Console.WriteLine("Parent has completed.")
   End Sub
End Module
' The example displays output like the following:
'       Parent task executing
'       Parent has completed.
'       Attached child starting.

Exceptions dans les tâches enfantsExceptions in child tasks

Si une tâche enfant détachée lève une exception, celle-ci doit être observée ou gérée directement dans la tâche parente, comme dans le cas de n’importe quelle tâche non imbriquée.If a detached child task throws an exception, that exception must be observed or handled directly in the parent task just as with any non-nested task. Si une tâche enfant attachée lève une exception, celle-ci est automatiquement propagée vers la tâche parente, puis vers le thread qui attend ou essaie d’accéder à la propriété Task<TResult>.Result de la tâche.If an attached child task throws an exception, the exception is automatically propagated to the parent task and back to the thread that waits or tries to access the task's Task<TResult>.Result property. Ainsi, en utilisant des tâches enfants attachées, vous pouvez gérer toutes les exceptions en un seul point dans l’appel à Task.Wait sur le thread appelant.Therefore, by using attached child tasks, you can handle all exceptions at just one point in the call to Task.Wait on the calling thread. Pour plus d’informations, consultez l’article Gestion des exceptions.For more information, see Exception Handling.

Annulation et tâches enfantsCancellation and child tasks

L'annulation de tâche est coopérative.Task cancellation is cooperative. Autrement dit, pour être annulable, chaque tâche enfant attachée ou détachée doit surveiller l'état du jeton d'annulation.That is, to be cancelable, every attached or detached child task must monitor the status of the cancellation token. Pour annuler un parent et tous ses enfants à l'aide d'une demande d'annulation, vous passez le même jeton en tant qu'argument à toutes les tâches et fournissez dans chaque tâche la logique pour répondre à la demande.If you want to cancel a parent and all its children by using one cancellation request, you pass the same token as an argument to all tasks and provide in each task the logic to respond to the request in each task. Pour plus d’informations, consultez Annulation de tâches et Comment : annuler une tâche et ses enfants.For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

Annulation d'un parentWhen the parent cancels

Si un parent s’annule avant le démarrage de sa tâche enfant, celle-ci ne démarre jamais.If a parent cancels itself before its child task is started, the child never starts. Si un parent s’annule une fois que sa tâche enfant a démarré, celle-ci s’exécute jusqu’à son terme sauf si elle possède sa propre logique d’annulation.If a parent cancels itself after its child task has already started, the child runs to completion unless it has its own cancellation logic. Pour plus d’informations, voir Annulation de tâches.For more information, see Task Cancellation.

Annulation d’une tâche enfant détachéeWhen a detached child task cancels

Si une tâche enfant détachée s’annule à l’aide du jeton passé au parent et que celui-ci n’attend pas la tâche enfant, aucune exception n’est propagée, car l’exception est traitée comme une annulation de coopération bénigne.If a detached child task cancels itself by using the same token that was passed to the parent, and the parent does not wait for the child task, no exception is propagated, because the exception is treated as benign cooperation cancellation. Ce comportement est identique à celui de toute tâche de niveau supérieur.This behavior is the same as that of any top-level task.

Annulation d’une tâche enfant attachéeWhen an attached child task cancels

Quand une tâche enfant attachée s’annule à l’aide du jeton passé à sa tâche parente, une TaskCanceledException est propagée vers le thread intermédiaire à l’intérieur d’une AggregateException.When an attached child task cancels itself by using the same token that was passed to its parent task, a TaskCanceledException is propagated to the joining thread inside an AggregateException. Vous devez attendre la tâche parente pour 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.You must wait for the parent task so that you can handle all benign exceptions in addition to all faulting exceptions that are propagated up through a graph of attached child tasks.

Pour plus d’informations, consultez l’article Gestion des exceptions.For more information, see Exception Handling.

Empêcher qu'une tâche enfant ne s'attache à son parentPreventing a child task from attaching to its parent

Une exception non gérée levée par une tâche enfant est propagée vers la tâche parente.An unhandled exception that is thrown by a child task is propagated to the parent task. Vous pouvez vous baser sur ce comportement pour observer toutes les exceptions de tâche enfant à partir d'une seule tâche racine au lieu de parcourir une arborescence de tâches.You can use this behavior to observe all child task exceptions from one root task instead of traversing a tree of tasks. Toutefois, la propagation d’exception peut être problématique quand une tâche parente n’attend pas d’attachement de la part d’un autre code.However, exception propagation can be problematic when a parent task does not expect attachment from other code. Par exemple, imaginez une application qui appelle un composant de bibliothèque tierce à partir d'un objet Task.For example, consider an app that calls a third-party library component from a Task object. Si ce composant crée également un objet Task et spécifie TaskCreationOptions.AttachedToParent pour l’attacher à la tâche parente, les exceptions non gérées qui se produisent dans la tâche enfant se propagent vers le parent.If the third-party library component also creates a Task object and specifies TaskCreationOptions.AttachedToParent to attach it to the parent task, any unhandled exceptions that occur in the child task propagate to the parent. Cela peut entraîner un comportement inattendu dans l'application principale.This could lead to unexpected behavior in the main app.

Pour empêcher une tâche enfant de s'attacher à sa tâche parente, spécifiez l'option TaskCreationOptions.DenyChildAttach quand vous créez l'objet Task ou Task<TResult> parent.To prevent a child task from attaching to its parent task, specify the TaskCreationOptions.DenyChildAttach option when you create the parent Task or Task<TResult> object. Si une tâche enfant tente de s’attacher à son parent alors que celui-ci spécifie l’option TaskCreationOptions.DenyChildAttach, elle échoue et s’exécute comme si l’option TaskCreationOptions.AttachedToParent n’était pas spécifiée.When a task tries to attach to its parent and the parent specifies the TaskCreationOptions.DenyChildAttach option, the child task will not be able to attach to a parent and will execute just as if the TaskCreationOptions.AttachedToParent option was not specified.

Vous pourriez également empêcher une tâche enfant de s'attacher à son parent quand la tâche enfant ne se termine pas en temps voulu.You might also want to prevent a child task from attaching to its parent when the child task does not finish in a timely manner. Étant donné qu’une tâche parente ne se termine pas tant que toutes les tâches enfants ne sont pas achevées, une tâche enfant à exécution longue peut entraîner des performances médiocres de la part de l’application globale.Because a parent task does not finish until all child tasks finish, a long-running child task can cause the overall app to perform poorly. Pour obtenir un exemple qui montre comment améliorer les performances de l’application en empêchant une tâche de s’attacher à sa tâche parente, consultez Procédure : empêcher une tâche enfant de s’attacher à son parent.For an example that shows how to improve app performance by preventing a task from attaching to its parent task, see How to: Prevent a Child Task from Attaching to its Parent.

Voir aussiSee also