Attività figlio connesse e disconnesseAttached and Detached Child Tasks

Un'attività figlio (o attività annidata) è un'istanza System.Threading.Tasks.Task creata nel delegato dell'utente di un'altra attività, noto come attività padre.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. Un'attività figlio può essere scollegata o collegata.A child task can be either detached or attached. Un'attività figlio scollegata è un'attività eseguita indipendentemente dall'attività padre.A detached child task is a task that executes independently of its parent. Un'attività figlio collegata è un'attività annidata creata con l'opzione TaskCreationOptions.AttachedToParent alla quale l'attività padre non impedisce il collegamento in modo esplicito o per impostazione predefinita.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. Un'attività può creare un numero qualsiasi di attività figlio collegate e scollegate, limitato solo dalle risorse di sistema.A task may create any number of attached and detached child tasks, limited only by system resources.

La tabella seguente elenca le differenze di base tra i due tipi di attività figlio.The following table lists the basic differences between the two kinds of child tasks.

CategoryCategory Attività figlio scollegateDetached child tasks Attività figlio collegateAttached child tasks
L'attività padre attende il completamento delle attività figlio.Parent waits for child tasks to complete. NoNo YesYes
L'attività padre propaga le eccezioni generate dalle attività figlio.Parent propagates exceptions thrown by child tasks. NoNo YesYes
Lo stato dell'attività padre dipende dallo stato dell'attività figlio.Status of parent depends on status of child. NoNo YesYes

Nella maggior parte degli scenari, è consigliabile usare l'attività figlio scollegata perché le relazioni con altre attività risultano meno complesse.In most scenarios, we recommend that you use detached child tasks, because their relationships with other tasks are less complex. Per questo motivo le attività create all'interno delle attività padre sono di tipo scollegato per impostazione predefinita ed è necessario specificare esplicitamente l'opzione TaskCreationOptions.AttachedToParent per creare un'attività figlio collegata.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.

Attività figlio scollegateDetached child tasks

Anche se un'attività figlio viene creata da un'attività padre, per impostazione predefinita è indipendente dell'attività padre.Although a child task is created by a parent task, by default it is independent of the parent task. Nell'esempio seguente, un'attività padre crea un'attività figlio semplice.In the following example, a parent task creates one simple child task. Se si esegue più volte il codice di esempio, è possibile notare che l'output dell'esempio è diverso da quello indicato e potrebbe cambiare ogni volta che si esegue il codice.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. Il motivo è che l'attività padre e le attività figlio sono eseguite in modo indipendente; l'attività figlio è di tipo scollegato.This occurs because the parent task and child tasks execute independently of each other; the child is a detached task. Nell'esempio si attende solo il completamento dell'attività padre, mentre l'attività figlio potrebbe non essere eseguita o completata prima del termine dell'applicazione 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.

Se l'attività figlio è rappresentata da un oggetto Task<TResult> anziché da un oggetto Task, è possibile assicurarsi che l'attività padre attenda il completamento dell'attività figlio accedendo alla proprietà Task<TResult>.Result dell'attività figlio anche è di tipo scollegato.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 proprietà Result viene bloccata fino al completamento dell'attività, come mostrato nell'esempio seguente.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
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

Attività figlio collegateAttached child tasks

A differenza delle attività figlio scollegate, le attività figlio collegate sono strettamente sincronizzate con l'attività padre.Unlike detached child tasks, attached child tasks are closely synchronized with the parent. È possibile modificare il tipo dell'attività figlio da scollegato a collegato usando l'opzione TaskCreationOptions.AttachedToParent nell'istruzione di creazione dell'attività, come mostrato nell'esempio seguente.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. In questo codice, l'attività figlio collegata viene completata prima dell'attività padre.In this code, the attached child task completes before its parent. Di conseguenza, l'output dell'esempio è lo stesso per ogni esecuzione del codice.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.

Per creare grafici strettamente sincronizzati di operazioni asincrone, è possibile usare le attività figlio collegate.You can use attached child tasks to create tightly synchronized graphs of asynchronous operations.

Tuttavia, un'attività figlio può collegarsi all'attività padre solo se quest'ultimo consente le attività figlio collegate.However, a child task can attach to its parent only if its parent does not prohibit attached child tasks. Le attività padre possono impedire in modo esplicito il collegamento di attività figlio specificando l'opzione TaskCreationOptions.DenyChildAttach nel costruttore della classe dell'attività padre o il metodo 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. Le attività padre possono impedire in modo implicito il collegamento di attività figlio se sono state create chiamando il metodo Task.Run.Parent tasks implicitly prevent child tasks from attaching to them if they are created by calling the Task.Run method. Questa condizione è illustrata nell'esempio seguente.The following example illustrates this. L'esempio è uguale a quello precedente, ma in questo caso l'attività padre viene creata chiamando il metodo Task.Run(Action) anziché il metodo 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. Poiché l'attività figlio non è in grado di collegarsi all'attività padre, l'output dell'esempio è imprevedibile.Because the child task is not able to attach to its parent, the output from the example is unpredictable. Poiché le opzioni di creazione dell'attività predefinite per gli overload Task.Run includono TaskCreationOptions.DenyChildAttach, questo esempio è funzionalmente equivalente al primo esempio nella sezione "Attività figlio scollegate".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.

Eccezioni nelle attività figlioExceptions in child tasks

Se un'attività figlio scollegata genera un'eccezione, l'eccezione deve essere osservata o gestita direttamente nell'attività padre come accade nel caso di qualsiasi attività non annidata.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. Se un'attività figlio collegata genera un'eccezione, l'eccezione viene propagata automaticamente all'attività padre e al thread che rimane in attesa o tenta di accedere alla proprietà Task<TResult>.Result dell'attività.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. Quindi, usando le attività figlio collegate è possibile gestire tutte le eccezioni in un solo punto della chiamata a Task.Wait nel thread chiamante.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. Per altre informazioni, vedere Gestione delle eccezioni.For more information, see Exception Handling.

Annullamento e attività figlioCancellation and child tasks

L'annullamento delle attività è cooperativo,Task cancellation is cooperative. ossia, per essere annullabile, ogni attività figlio collegata o scollegata deve monitorare lo stato del token di annullamento.That is, to be cancelable, every attached or detached child task must monitor the status of the cancellation token. Per annullare un'attività padre e le relative attività figlio usando un'unica richiesta, è necessario passare lo stesso token come argomento a tutte le attività e fornire la logica di risposta alla richiesta in ogni attività.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. Per altre informazioni, vedere Annullamento delle attività e Procedura: Annullare un'attività e i relativi figli.For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

Annullamento dell'attività padreWhen the parent cancels

Se un'attività padre annulla se stessa prima dell'avvio dell'attività figlio, quest'ultima non viene avviata.If a parent cancels itself before its child task is started, the child never starts. Se un'attività padre annulla se stessa dopo l'avvio dell'attività figlio, quest'ultima viene completata a meno che non abbia una propria logica di annullamento.If a parent cancels itself after its child task has already started, the child runs to completion unless it has its own cancellation logic. Per altre informazioni, vedere Annullamento delle attività.For more information, see Task Cancellation.

Annullamento di un'attività figlio scollegataWhen a detached child task cancels

Se un'attività figlio scollegata annulla se stessa usando lo stesso token passato all'attività padre e quest'ultima non attende l'attività figlio, non viene propagata alcuna eccezione poiché l'eccezione viene considerata come un annullamento cooperativo sicuro.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. Questo comportamento è uguale a quello di qualsiasi attività di primo livello.This behavior is the same as that of any top-level task.

Annullamento di un'attività figlio collegataWhen an attached child task cancels

Quando un'attività figlio collegata annulla se stessa usando lo stesso token passato all'attività padre, TaskCanceledException viene propagato al thread di unione all'interno di 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. È necessario attendere l'attività padre in modo che sia possibile gestire tutte le eccezioni sicure oltre a tutte le eccezioni di errore propagate tramite un grafico di attività figlio collegate.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.

Per altre informazioni, vedere Gestione delle eccezioni.For more information, see Exception Handling.

Impedire il collegamento di un'attività figlio all'attività padrePreventing a child task from attaching to its parent

Un'eccezione non gestita generata da un'attività figlio viene propagata all'attività padre.An unhandled exception that is thrown by a child task is propagated to the parent task. È possibile usare questo comportamento per osservare tutte le eccezioni dell'attività figlio da un'attività radice invece di passare per un albero delle attività.You can use this behavior to observe all child task exceptions from one root task instead of traversing a tree of tasks. Tuttavia, la propagazione delle eccezioni può essere problematica quando un'attività padre non prevede un allegato da altro codice.However, exception propagation can be problematic when a parent task does not expect attachment from other code. Si consideri ad esempio un'applicazione che chiama un componente della libreria di terze parti da un oggetto Task.For example, consider an app that calls a third-party library component from a Task object. Se il componente della libreria di terze parti crea anche un oggetto Task e specifica TaskCreationOptions.AttachedToParent per collegarlo all'attività padre, le eventuali eccezioni non gestite che si verificano nell'attività figlio vengono propagate all'attività padre.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. Questo potrebbe causare un comportamento imprevisto nell'applicazione principale.This could lead to unexpected behavior in the main app.

Per impedire il collegamento di un'attività figlio all'attività padre, specificare l'opzione TaskCreationOptions.DenyChildAttach quando si crea l'attività padre Task o l'oggetto Task<TResult>.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. Quando un'attività tenta di connettersi all'attività padre che specifica l'opzione TaskCreationOptions.DenyChildAttach, l'attività figlio non riuscirà a collegarsi a un'attività padre e verrà eseguita come se l'opzione TaskCreationOptions.AttachedToParent non fosse stata specificata.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.

Se l'attività figlio non viene completata in modo tempestivo, è anche possibile impedirne il collegamento all'attività padre.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. Poiché un'attività padre non viene completata fino al completamento di tutte le attività figlio, un'attività figlio a esecuzione prolungata può influire negativamente sulle prestazioni dell'applicazione.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. Per un esempio in cui viene spiegato come migliorare le prestazioni dell'applicazione impedendo il collegamento di un'attività all'attività padre, vedere Procedura: Impedire il collegamento di un'attività figlio all'attività padre.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.

Vedere ancheSee Also

Programmazione parallelaParallel Programming
Parallelismo dei datiData Parallelism