Concatenamento di attività tramite attività di continuazioneChaining Tasks by Using Continuation Tasks

Nella programmazione asincrona è comune che, dopo il completamento, un'operazione asincrona chiami una seconda operazione e passi i dati a tale operazione.In asynchronous programming, it is common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. Tradizionalmente le continuazioni venivano eseguite tramite metodi di callback.Traditionally, continuations have been done by using callback methods. In Task Parallel Library la stessa funzionalità viene resa possibile dalle attività di continuazione.In the Task Parallel Library, the same functionality is provided by continuation tasks. Un'attivazione di continuazione, chiamata anche semplicemente continuazione, è un'attività asincrona richiamata da un'altra attività, denominata attività precedente, al termine di quest'ultima.A continuation task (also known just as a continuation) is an asynchronous task that is invoked by another task, which is known as the antecedent, when the antecedent finishes.

Le continuazioni sono relativamente facili da usare, ma sono comunque potenti e flessibili.Continuations are relatively easy to use, but are nevertheless powerful and flexible. Ad esempio, è possibile eseguire queste operazioni:For example, you can:

  • Passare dati dall'attività precedente alla continuazione.Pass data from the antecedent to the continuation.

  • Specificare le condizioni esatte in cui la continuazione verrà richiamata o non richiamata.Specify the precise conditions under which the continuation will be invoked or not invoked.

  • Annullare una continuazione prima del suo avvio o cooperativamente durante la sua esecuzione.Cancel a continuation either before it starts or cooperatively as it is running.

  • Fornire suggerimenti sul modo in cui pianificare la continuazione.Provide hints about how the continuation should be scheduled.

  • Richiamare più continuazioni dalla stessa attività precedente.Invoke multiple continuations from the same antecedent.

  • Richiamare una continuazione al termine di tutte o una delle attività precedenti.Invoke one continuation when all or any one of multiple antecedents complete.

  • Concatenare continuazioni una dopo l'altra a qualsiasi lunghezza arbitraria.Chain continuations one after another to any arbitrary length.

  • Usare una continuazione per gestire le eccezioni generate dall'attività precedente.Use a continuation to handle exceptions thrown by the antecedent.

Informazioni sulle continuazioniAbout continuations

Una continuazione è un'attività creata nello stato WaitingForActivation .A continuation is a task that is created in the WaitingForActivation state. Questa attività viene automaticamente attivata al termine della o delle attività precedenti.It is activated automatically when its antecedent task or tasks complete. Se si chiama Task.Start in una continuazione nel codice utente, viene generata un'eccezione System.InvalidOperationException .Calling Task.Start on a continuation in user code throws an System.InvalidOperationException exception.

Una continuazione è essa stessa un oggetto Task e non blocca il thread su cui viene avviata.A continuation is itself a Task and does not block the thread on which it is started. Chiamare il metodo Task.Wait per bloccare il thread fino al termine dell'attività di continuazione.Call the Task.Wait method to block until the continuation task finishes.

Creazione di una continuazione per una singola attività precedenteCreating a continuation for a single antecedent

Per creare una continuazione che viene eseguita una volta completata l'attività precedente, chiamare il metodo Task.ContinueWith .You create a continuation that executes when its antecedent has completed by calling the Task.ContinueWith method. L'esempio seguente mostra il modello di base. Per chiarezza, viene omessa la gestione delle eccezioni.The following example shows the basic pattern (for clarity, exception handling is omitted). Viene eseguita un'attività precedente, taskA, che restituisce un oggetto DayOfWeek che indica il nome del giorno corrente della settimana.It executes an antecedent task, taskA, that returns a DayOfWeek object that indicates the name of the current day of the week. Al termine dell'attività precedente, all'attività di continuazione continuation viene passata l'attività precedente e viene visualizzata una stringa che ne include i risultati.When the antecedent completes, the continuation task, continuation, is passed the antecedent and displays a string that includes its result.

Nota

Gli esempi C# in questo articolo usano il modificatore async nel metodo Main.The C# samples in this article make use of the async modifier on the Main method. Questa funzionalità è disponibile in C# 7.1 e versioni successive.That feature is available in C# 7.1 and later. Le versioni precedenti generano CS5001 quando si compila questo codice di esempio.Previous versions generate CS5001 when compiling this sample code. È necessario impostare la versione del linguaggio C# su 7.1 o versione successiva.You'll need to set the language version to C# 7.1 or newer. Per istruzioni, vedere l'articolo sulla configurazione della versione del linguaggio.You can learn how to configure the language version in the article on configure language version.

using System;
using System.Threading.Tasks;

public class Example
{
   public static async Task Main()
   {
      // Execute the antecedent.
      Task<DayOfWeek> taskA = Task.Run( () => DateTime.Today.DayOfWeek );

      // Execute the continuation when the antecedent finishes.
      await taskA.ContinueWith( antecedent => Console.WriteLine("Today is {0}.", antecedent.Result) );
   }
}
// The example displays output like the following output:
//       Today is Monday.
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      ' Execute the antecedent.
      Dim taskA As Task(Of DayOfWeek) = Task.Run(Function() DateTime.Today.DayOfWeek )

      ' Execute the continuation when the antecedent finishes.
      Dim continuation As Task = taskA.ContinueWith(Sub(antecedent)
                                                       Console.WriteLine("Today is {0}.", antecedent.Result)
                                                    End Sub)
      continuation.Wait()
   End Sub
End Module
' The example displays output like the following output:
'       Today is Monday.

Creazione di una continuazione per più attività precedentiCreating a continuation for multiple antecedents

È anche possibile creare una continuazione che verrà eseguita al termine di una o tutte le attività di un gruppo di attività.You can also create a continuation that will run when any or all of a group of tasks has completed. Per eseguire una continuazione al termine di tutte le attività precedenti, chiamare il metodo statico (Shared in Visual Basic) Task.WhenAll o il metodo TaskFactory.ContinueWhenAll dell'istanza.To execute a continuation when all antecedent tasks have completed, you call the static (Shared in Visual Basic) Task.WhenAll method or the instance TaskFactory.ContinueWhenAll method. Per eseguire una continuazione al termine delle attività precedenti, chiamare il metodo statico (Shared in Visual Basic) Task.WhenAny o il metodo TaskFactory.ContinueWhenAny dell'istanza.To execute a continuation when any of the antecedent tasks has completed, you call the static (Shared in Visual Basic) Task.WhenAny method or the instance TaskFactory.ContinueWhenAny method.

Si noti che le chiamate agli overload Task.WhenAll e Task.WhenAny non bloccano il thread chiamante.Note that calls to the Task.WhenAll and Task.WhenAny overloads do not block the calling thread. Tuttavia, in genere si eseguono chiamate a tutti gli elementi a eccezione dei metodi Task.WhenAll(IEnumerable<Task>) e Task.WhenAll(Task[]) per recuperare la proprietà Task<TResult>.Result restituita, che blocca il thread chiamante.However, you typically call all but the Task.WhenAll(IEnumerable<Task>) and Task.WhenAll(Task[]) methods to retrieve the returned Task<TResult>.Result property, which does block the calling thread.

Nell'esempio seguente viene chiamato il metodo Task.WhenAll(IEnumerable<Task>) per creare un'attività di continuazione che riflette il risultato delle 10 attività precedenti.The following example calls the Task.WhenAll(IEnumerable<Task>) method to create a continuation task that reflects the results of its 10 antecedent tasks. Ogni attività precedente eleva al quadrato un valore di indice compreso tra uno e 10.Each antecedent task squares an index value that ranges from one to 10. Se le attività precedenti vengono completate correttamente (ovvero la relativa proprietà Task.Status è TaskStatus.RanToCompletion), la proprietà Task<TResult>.Result della continuazione è una matrice dei valori Task<TResult>.Result restituiti da ogni attività precedente.If the antecedents complete successfully (their Task.Status property is TaskStatus.RanToCompletion), the Task<TResult>.Result property of the continuation is an array of the Task<TResult>.Result values returned by each antecedent. L'esempio somma tali valori per calcolare la somma dei quadrati per tutti i numeri compresi tra uno e 10.The example adds them to compute the sum of squares for all numbers between one and 10.

using System.Collections.Generic;
using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      List<Task<int>> tasks = new List<Task<int>>();
      for (int ctr = 1; ctr <= 10; ctr++) {
         int baseValue = ctr;
         tasks.Add(Task.Factory.StartNew( (b) => { int i = (int) b;
                                                   return i * i; }, baseValue));
      }
      var continuation = Task.WhenAll(tasks);

      long sum = 0;
      for (int ctr = 0; ctr <= continuation.Result.Length - 1; ctr++) {
         Console.Write("{0} {1} ", continuation.Result[ctr],
                       ctr == continuation.Result.Length - 1 ? "=" : "+");
         sum += continuation.Result[ctr];
      }
      Console.WriteLine(sum);
   }
}
// The example displays the following output:
//    1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100 = 385
Imports System.Collections.Generic
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim tasks As New List(Of Task(Of Integer))()
      For ctr As Integer = 1 To 10
         Dim baseValue As Integer = ctr
         tasks.Add(Task.Factory.StartNew( Function(b)
                                             Dim i As Integer = CInt(b)
                                             Return i * i
                                          End Function, baseValue))
      Next
      Dim continuation = Task.WhenAll(tasks)

      Dim sum As Long = 0
      For ctr As Integer = 0 To continuation.Result.Length - 1
         Console.Write("{0} {1} ", continuation.Result(ctr),
                       If (ctr = continuation.Result.Length - 1, "=", "+"))
         sum += continuation.Result(ctr)
      Next
      Console.WriteLine(sum)
   End Sub
End Module
' The example displays the following output:
'       1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100 = 385

Opzioni per le continuazioniContinuation options

Quando si crea un continuazione ad attività singola, è possibile usare un overload ContinueWith che accetta un valore di enumerazione System.Threading.Tasks.TaskContinuationOptions per specificare le condizioni in cui viene avviata la continuazione.When you create a single-task continuation, you can use a ContinueWith overload that takes a System.Threading.Tasks.TaskContinuationOptions enumeration value to specify the conditions under which the continuation starts. Ad esempio, è possibile specificare che la continuazione venga eseguita solo se l'attività precedente viene completata correttamente o se viene completata con uno stato di errore.For example, you can specify that the continuation is to run only if the antecedent completes successfully, or only if it completes in a faulted state. Se la condizione non è true quando l'attività precedente è pronta per chiamare la continuazione, questa passa direttamente allo stato TaskStatus.Canceled e successivamente non può più essere avviata.If the condition is not true when the antecedent is ready to invoke the continuation, the continuation transitions directly to the TaskStatus.Canceled state and subsequently cannot be started.

Alcuni metodi di continuazione multiattività, come gli overload del metodo TaskFactory.ContinueWhenAll , includono anche un parametro System.Threading.Tasks.TaskContinuationOptions .A number of multi-task continuation methods, such as overloads of the TaskFactory.ContinueWhenAll method, also include a System.Threading.Tasks.TaskContinuationOptions parameter. Tuttavia, è valido solo un subset di tutti i membri dell'enumerazione System.Threading.Tasks.TaskContinuationOptions .Only a subset of all System.Threading.Tasks.TaskContinuationOptions enumeration members are valid, however. È possibile specificare valori System.Threading.Tasks.TaskContinuationOptions per cui esistono controparti nell'enumerazione System.Threading.Tasks.TaskCreationOptions , come TaskContinuationOptions.AttachedToParent, TaskContinuationOptions.LongRunninge TaskContinuationOptions.PreferFairness.You can specify System.Threading.Tasks.TaskContinuationOptions values that have counterparts in the System.Threading.Tasks.TaskCreationOptions enumeration, such as TaskContinuationOptions.AttachedToParent, TaskContinuationOptions.LongRunning, and TaskContinuationOptions.PreferFairness. Se si specifica una qualsiasi delle opzioni NotOn o OnlyOn con una continuazione multiattività, viene generata un'eccezione ArgumentOutOfRangeException in fase di esecuzione.If you specify any of the NotOn or OnlyOn options with a multi-task continuation, an ArgumentOutOfRangeException exception will be thrown at run time.

Per altre informazioni sulle opzioni per la continuazione delle attività, vedere l'argomento TaskContinuationOptions .For more information on task continuation options, see the TaskContinuationOptions topic.

Passaggio di dati a una continuazionePassing Data to a Continuation

Il metodo Task.ContinueWith passa come argomento un riferimento all'attività precedente al delegato dell'utente della continuazione.The Task.ContinueWith method passes a reference to the antecedent to the user delegate of the continuation as an argument. Se l'attività precedente è un oggetto System.Threading.Tasks.Task<TResult> e l'attività è stata eseguita fino al suo completamento, la continuazione può accedere alla proprietà Task<TResult>.Result dell'attività.If the antecedent is a System.Threading.Tasks.Task<TResult> object, and the task ran until it was completed, then the continuation can access the Task<TResult>.Result property of the task.

La proprietà Task<TResult>.Result è bloccata fino al completamento dell'attività.The Task<TResult>.Result property blocks until the task has completed. Tuttavia, se l'attività è stata annullata o è in errore, il tentativo di accedere alla proprietà Result genera un'eccezione AggregateException .However, if the task was canceled or faulted, attempting to access the Result property throws an AggregateException exception. È possibile evitare questo problema usando l'opzione OnlyOnRanToCompletion , come mostrato nell'esempio seguente.You can avoid this problem by using the OnlyOnRanToCompletion option, as shown in the following example.

using System;
using System.Threading.Tasks;

public class Example
{
   public static async Task Main()
   {
      var t = Task.Run( () => { DateTime dat = DateTime.Now;
                                if (dat == DateTime.MinValue)
                                   throw new ArgumentException("The clock is not working.");
                                   
                                if (dat.Hour > 17)
                                   return "evening";
                                else if (dat.Hour > 12)
                                   return "afternoon";
                                else
                                   return "morning"; });
      await t.ContinueWith( (antecedent) => { Console.WriteLine("Good {0}!",
                                                                  antecedent.Result);
                                                Console.WriteLine("And how are you this fine {0}?",
                                                                  antecedent.Result); },
                              TaskContinuationOptions.OnlyOnRanToCompletion);
   }
}
// The example displays output like the following:
//       Good afternoon!
//       And how are you this fine afternoon?
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim t = Task.Run( Function()
                           Dim dat As DateTime = DateTime.Now
                           If dat = DateTime.MinValue Then
                              Throw New ArgumentException("The clock is not working.")
                           End If
                           
                           If dat.Hour > 17 Then
                              Return "evening"
                           Else If dat.Hour > 12 Then
                              Return "afternoon"
                           Else
                              Return "morning"
                           End If
                        End Function)
      Dim c = t.ContinueWith( Sub(antecedent)
                                 Console.WriteLine("Good {0}!",
                                                   antecedent.Result)
                                 Console.WriteLine("And how are you this fine {0}?",
                                                   antecedent.Result)
                              End Sub, TaskContinuationOptions.OnlyOnRanToCompletion)
      c.Wait()
   End Sub
End Module
' The example displays output like the following:
'       Good afternoon!
'       And how are you this fine afternoon?

Se si vuole che la continuazione venga eseguita anche se l'attività precedente non è stata eseguita fino al completamento, è necessario proteggersi dall'eccezione.If you want the continuation to run even if the antecedent did not run to successful completion, you must guard against the exception. Un approccio consiste nel testare la proprietà Task.Status dell'attività precedente e nel tentare di accedere alla proprietà Result solo se lo stato è diverso da Faulted o Canceled.One approach is to test the Task.Status property of the antecedent, and only attempt to access the Result property if the status is not Faulted or Canceled. È anche possibile esaminare la proprietà Exception dell'attività precedente.You can also examine the Exception property of the antecedent. Per altre informazioni, vedere Gestione delle eccezioni.For more information, see Exception Handling. Nell'esempio seguente viene modificato l'esempio precedente in modo da accedere alla proprietà Task<TResult>.Result dell'attività precedente solo se lo stato è TaskStatus.RanToCompletion.The following example modifies the previous example to access antecedent's Task<TResult>.Result property only if its status is TaskStatus.RanToCompletion.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var t = Task.Run( () => { DateTime dat = DateTime.Now;
                                if (dat == DateTime.MinValue)
                                   throw new ArgumentException("The clock is not working.");
                                   
                                if (dat.Hour > 17)
                                   return "evening";
                                else if (dat.Hour > 12)
                                   return "afternoon";
                                else
                                   return "morning"; });
      var c = t.ContinueWith( (antecedent) => { if (t.Status == TaskStatus.RanToCompletion) {
                                                   Console.WriteLine("Good {0}!",
                                                                     antecedent.Result);
                                                   Console.WriteLine("And how are you this fine {0}?",
                                                                  antecedent.Result);
                                                }
                                                else if (t.Status == TaskStatus.Faulted) {
                                                   Console.WriteLine(t.Exception.GetBaseException().Message);
                                                }} );
   }
}
// The example displays output like the following:
//       Good afternoon!
//       And how are you this fine afternoon?
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim t = Task.Run( Function()
                           Dim dat As DateTime = DateTime.Now
                           If dat = DateTime.MinValue Then
                              Throw New ArgumentException("The clock is not working.")
                           End If
                           
                           If dat.Hour > 17 Then
                              Return "evening"
                           Else If dat.Hour > 12 Then
                              Return "afternoon"
                           Else
                              Return "morning"
                           End If
                        End Function)
      Dim c = t.ContinueWith( Sub(antecedent)
                                 If t.Status = TaskStatus.RanToCompletion Then
                                    Console.WriteLine("Good {0}!",
                                                      antecedent.Result)
                                    Console.WriteLine("And how are you this fine {0}?",
                                                      antecedent.Result)
                                 Else If t.Status = TaskStatus.Faulted Then
                                    Console.WriteLine(t.Exception.GetBaseException().Message)
                                 End If
                              End Sub)
   End Sub
End Module
' The example displays output like the following:
'       Good afternoon!
'       And how are you this fine afternoon?

Annullamento di una continuazioneCanceling a Continuation

La proprietà Task.Status di una continuazione è impostata su TaskStatus.Canceled nelle situazioni seguenti:The Task.Status property of a continuation is set to TaskStatus.Canceled in the following situations:

Se un'attività e la relativa continuazione rappresentano due parti della stessa operazione logica, è possibile passare lo stesso token di annullamento a entrambe le attività, come mostrato nell'esempio seguente.If a task and its continuation represent two parts of the same logical operation, you can pass the same cancellation token to both tasks, as shown in the following example. L'esempio è costituito da un'attività precedente che genera un elenco di numeri interi divisibili per 33, che viene passato alla continuazione.It consists of an antecedent that generates a list of integers that are divisible by 33, which it passes to the continuation. La continuazione visualizza a sua volta l'elenco.The continuation in turn displays the list. L'attività precedente e la continuazione vengono entrambe sospese regolarmente per intervalli casuali.Both the antecedent and the continuation pause regularly for random intervals. Inoltre, viene usato un oggetto System.Threading.Timer per eseguire il metodo Elapsed dopo un intervallo di timeout di cinque secondi.In addition, a System.Threading.Timer object is used to execute the Elapsed method after a five-second timeout interval. Questo esempio chiama il metodo CancellationTokenSource.Cancel, che fa sì che l'attività attualmente in esecuzione chiami il metodo CancellationToken.ThrowIfCancellationRequested.This example calls the CancellationTokenSource.Cancel method, which causes the currently executing task to call the CancellationToken.ThrowIfCancellationRequested method. Il fatto che il metodo CancellationTokenSource.Cancel venga chiamato durante l'esecuzione dell'attività precedente o della sua continuazione dipende dalla durata delle pause generate casualmente.Whether the CancellationTokenSource.Cancel method is called when the antecedent or its continuation is executing depends on the duration of the randomly generated pauses. Se l'attività precedente viene annullata, la continuazione non verrà avviata.If the antecedent is canceled, the continuation will not start. Se l'attività precedente non viene annullata, il token può comunque essere usato per annullare la continuazione.If the antecedent is not canceled, the token can still be used to cancel the continuation.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Random rnd = new Random();
      var cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;
      Timer timer = new Timer(Elapsed, cts, 5000, Timeout.Infinite);

      var t = Task.Run( () => { List<int> product33 = new List<int>();
                                for (int ctr = 1; ctr < Int16.MaxValue; ctr++) {
                                   if (token.IsCancellationRequested) {
                                      Console.WriteLine("\nCancellation requested in antecedent...\n");
                                      token.ThrowIfCancellationRequested();
                                   }
                                   if (ctr % 2000 == 0) {
                                      int delay = rnd.Next(16,501);
                                      Thread.Sleep(delay);
                                   }

                                   if (ctr % 33 == 0)
                                      product33.Add(ctr);
                                }
                                return product33.ToArray();
                              }, token);

      Task continuation = t.ContinueWith(antecedent => { Console.WriteLine("Multiples of 33:\n");
                                                         var arr = antecedent.Result;
                                                         for (int ctr = 0; ctr < arr.Length; ctr++)
                                                         {
                                                            if (token.IsCancellationRequested) {
                                                               Console.WriteLine("\nCancellation requested in continuation...\n");
                                                               token.ThrowIfCancellationRequested();
                                                            }

                                                            if (ctr % 100 == 0) {
                                                               int delay = rnd.Next(16,251);
                                                               Thread.Sleep(delay);
                                                            }
                                                            Console.Write("{0:N0}{1}", arr[ctr],
                                                                          ctr != arr.Length - 1 ? ", " : "");
                                                            if (Console.CursorLeft >= 74)
                                                               Console.WriteLine();
                                                         }
                                                         Console.WriteLine();
                                                       } , token);

      try {
          continuation.Wait();
      }
      catch (AggregateException e) {
         foreach (Exception ie in e.InnerExceptions)
            Console.WriteLine("{0}: {1}", ie.GetType().Name,
                              ie.Message);
      }
      finally {
         cts.Dispose();
      }

      Console.WriteLine("\nAntecedent Status: {0}", t.Status);
      Console.WriteLine("Continuation Status: {0}", continuation.Status);
  }

   private static void Elapsed(object state)
   {
      CancellationTokenSource cts = state as CancellationTokenSource;
      if (cts == null) return;

      cts.Cancel();
      Console.WriteLine("\nCancellation request issued...\n");
   }
}
// The example displays the following output:
//    Multiples of 33:
//
//    33, 66, 99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462, 495, 528,
//    561, 594, 627, 660, 693, 726, 759, 792, 825, 858, 891, 924, 957, 990, 1,023,
//    1,056, 1,089, 1,122, 1,155, 1,188, 1,221, 1,254, 1,287, 1,320, 1,353, 1,386,
//    1,419, 1,452, 1,485, 1,518, 1,551, 1,584, 1,617, 1,650, 1,683, 1,716, 1,749,
//    1,782, 1,815, 1,848, 1,881, 1,914, 1,947, 1,980, 2,013, 2,046, 2,079, 2,112,
//    2,145, 2,178, 2,211, 2,244, 2,277, 2,310, 2,343, 2,376, 2,409, 2,442, 2,475,
//    2,508, 2,541, 2,574, 2,607, 2,640, 2,673, 2,706, 2,739, 2,772, 2,805, 2,838,
//    2,871, 2,904, 2,937, 2,970, 3,003, 3,036, 3,069, 3,102, 3,135, 3,168, 3,201,
//    3,234, 3,267, 3,300, 3,333, 3,366, 3,399, 3,432, 3,465, 3,498, 3,531, 3,564,
//    3,597, 3,630, 3,663, 3,696, 3,729, 3,762, 3,795, 3,828, 3,861, 3,894, 3,927,
//    3,960, 3,993, 4,026, 4,059, 4,092, 4,125, 4,158, 4,191, 4,224, 4,257, 4,290,
//    4,323, 4,356, 4,389, 4,422, 4,455, 4,488, 4,521, 4,554, 4,587, 4,620, 4,653,
//    4,686, 4,719, 4,752, 4,785, 4,818, 4,851, 4,884, 4,917, 4,950, 4,983, 5,016,
//    5,049, 5,082, 5,115, 5,148, 5,181, 5,214, 5,247, 5,280, 5,313, 5,346, 5,379,
//    5,412, 5,445, 5,478, 5,511, 5,544, 5,577, 5,610, 5,643, 5,676, 5,709, 5,742,
//    5,775, 5,808, 5,841, 5,874, 5,907, 5,940, 5,973, 6,006, 6,039, 6,072, 6,105,
//    6,138, 6,171, 6,204, 6,237, 6,270, 6,303, 6,336, 6,369, 6,402, 6,435, 6,468,
//    6,501, 6,534, 6,567, 6,600, 6,633, 6,666, 6,699, 6,732, 6,765, 6,798, 6,831,
//    6,864, 6,897, 6,930, 6,963, 6,996, 7,029, 7,062, 7,095, 7,128, 7,161, 7,194,
//    7,227, 7,260, 7,293, 7,326, 7,359, 7,392, 7,425, 7,458, 7,491, 7,524, 7,557,
//    7,590, 7,623, 7,656, 7,689, 7,722, 7,755, 7,788, 7,821, 7,854, 7,887, 7,920,
//    7,953, 7,986, 8,019, 8,052, 8,085, 8,118, 8,151, 8,184, 8,217, 8,250, 8,283,
//    8,316, 8,349, 8,382, 8,415, 8,448, 8,481, 8,514, 8,547, 8,580, 8,613, 8,646,
//    8,679, 8,712, 8,745, 8,778, 8,811, 8,844, 8,877, 8,910, 8,943, 8,976, 9,009,
//    9,042, 9,075, 9,108, 9,141, 9,174, 9,207, 9,240, 9,273, 9,306, 9,339, 9,372,
//    9,405, 9,438, 9,471, 9,504, 9,537, 9,570, 9,603, 9,636, 9,669, 9,702, 9,735,
//    9,768, 9,801, 9,834, 9,867, 9,900, 9,933, 9,966, 9,999, 10,032, 10,065, 10,098,
//    10,131, 10,164, 10,197, 10,230, 10,263, 10,296, 10,329, 10,362, 10,395, 10,428,
//    10,461, 10,494, 10,527, 10,560, 10,593, 10,626, 10,659, 10,692, 10,725, 10,758,
//    10,791, 10,824, 10,857, 10,890, 10,923, 10,956, 10,989, 11,022, 11,055, 11,088,
//    11,121, 11,154, 11,187, 11,220, 11,253, 11,286, 11,319, 11,352, 11,385, 11,418,
//    11,451, 11,484, 11,517, 11,550, 11,583, 11,616, 11,649, 11,682, 11,715, 11,748,
//    11,781, 11,814, 11,847, 11,880, 11,913, 11,946, 11,979, 12,012, 12,045, 12,078,
//    12,111, 12,144, 12,177, 12,210, 12,243, 12,276, 12,309, 12,342, 12,375, 12,408,
//    12,441, 12,474, 12,507, 12,540, 12,573, 12,606, 12,639, 12,672, 12,705, 12,738,
//    12,771, 12,804, 12,837, 12,870, 12,903, 12,936, 12,969, 13,002, 13,035, 13,068,
//    13,101, 13,134, 13,167, 13,200, 13,233, 13,266,
//    Cancellation requested in continuation...
//
//
//    Cancellation request issued...
//
//    TaskCanceledException: A task was canceled.
//
//    Antecedent Status: RanToCompletion
//    Continuation Status: Canceled
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim rnd As New Random()
      Dim lockObj As New Object()
      Dim cts As New CancellationTokenSource()
      Dim token As CancellationToken = cts.Token
      Dim timer As New Timer(AddressOf Elapsed, cts, 5000, Timeout.Infinite)

      Dim t = Task.Run( Function()
                           Dim product33 As New List(Of Integer)()
                           For ctr As Integer = 1 To Int16.MaxValue
                              ' Check for cancellation.
                              If token.IsCancellationRequested Then
                                 Console.WriteLine("\nCancellation requested in antecedent...\n")
                                 token.ThrowIfCancellationRequested()
                              End If
                              ' Introduce a delay.
                              If ctr Mod 2000 = 0 Then
                                 Dim delay As Integer
                                 SyncLock lockObj
                                    delay = rnd.Next(16,501)
                                 End SyncLock
                                 Thread.Sleep(delay)
                              End If

                              ' Determine if this is a multiple of 33.
                              If ctr Mod 33 = 0 Then product33.Add(ctr)
                           Next
                           Return product33.ToArray()
                        End Function, token)

      Dim continuation = t.ContinueWith(Sub(antecedent)
                                           Console.WriteLine("Multiples of 33:" + vbCrLf)
                                           Dim arr = antecedent.Result
                                           For ctr As Integer = 0 To arr.Length - 1
                                              If token.IsCancellationRequested Then
                                                 Console.WriteLine("{0}Cancellation requested in continuation...{0}",
                                                                   vbCrLf)
                                                 token.ThrowIfCancellationRequested()
                                              End If

                                              If ctr Mod 100 = 0 Then
                                                 Dim delay As Integer
                                                 SyncLock lockObj
                                                    delay = rnd.Next(16,251)
                                                 End SyncLock
                                                 Thread.Sleep(delay)
                                              End If
                                              Console.Write("{0:N0}{1}", arr(ctr),
                                                            If(ctr <> arr.Length - 1, ", ", ""))
                                              If Console.CursorLeft >= 74 Then Console.WriteLine()
                                           Next
                                           Console.WriteLine()
                                        End Sub, token)

      Try
         continuation.Wait()
      Catch e As AggregateException
         For Each ie In e.InnerExceptions
            Console.WriteLine("{0}: {1}", ie.GetType().Name,
                              ie.Message)
         Next
      Finally
         cts.Dispose()
      End Try

      Console.WriteLine(vbCrLf + "Antecedent Status: {0}", t.Status)
      Console.WriteLine("Continuation Status: {0}", continuation.Status)
  End Sub

   Private Sub Elapsed(state As Object)
      Dim cts As CancellationTokenSource = TryCast(state, CancellationTokenSource)
      If cts Is Nothing Then return

      cts.Cancel()
      Console.WriteLine("{0}Cancellation request issued...{0}", vbCrLf)
   End Sub
End Module
' The example displays output like the following:
'    Multiples of 33:
'
'    33, 66, 99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462, 495, 528,
'    561, 594, 627, 660, 693, 726, 759, 792, 825, 858, 891, 924, 957, 990, 1,023,
'    1,056, 1,089, 1,122, 1,155, 1,188, 1,221, 1,254, 1,287, 1,320, 1,353, 1,386,
'    1,419, 1,452, 1,485, 1,518, 1,551, 1,584, 1,617, 1,650, 1,683, 1,716, 1,749,
'    1,782, 1,815, 1,848, 1,881, 1,914, 1,947, 1,980, 2,013, 2,046, 2,079, 2,112,
'    2,145, 2,178, 2,211, 2,244, 2,277, 2,310, 2,343, 2,376, 2,409, 2,442, 2,475,
'    2,508, 2,541, 2,574, 2,607, 2,640, 2,673, 2,706, 2,739, 2,772, 2,805, 2,838,
'    2,871, 2,904, 2,937, 2,970, 3,003, 3,036, 3,069, 3,102, 3,135, 3,168, 3,201,
'    3,234, 3,267, 3,300, 3,333, 3,366, 3,399, 3,432, 3,465, 3,498, 3,531, 3,564,
'    3,597, 3,630, 3,663, 3,696, 3,729, 3,762, 3,795, 3,828, 3,861, 3,894, 3,927,
'    3,960, 3,993, 4,026, 4,059, 4,092, 4,125, 4,158, 4,191, 4,224, 4,257, 4,290,
'    4,323, 4,356, 4,389, 4,422, 4,455, 4,488, 4,521, 4,554, 4,587, 4,620, 4,653,
'    4,686, 4,719, 4,752, 4,785, 4,818, 4,851, 4,884, 4,917, 4,950, 4,983, 5,016,
'    5,049, 5,082, 5,115, 5,148, 5,181, 5,214, 5,247, 5,280, 5,313, 5,346, 5,379,
'    5,412, 5,445, 5,478, 5,511, 5,544, 5,577, 5,610, 5,643, 5,676, 5,709, 5,742,
'    5,775, 5,808, 5,841, 5,874, 5,907, 5,940, 5,973, 6,006, 6,039, 6,072, 6,105,
'    6,138, 6,171, 6,204, 6,237, 6,270, 6,303, 6,336, 6,369, 6,402, 6,435, 6,468,
'    6,501, 6,534, 6,567, 6,600, 6,633, 6,666, 6,699, 6,732, 6,765, 6,798, 6,831,
'    6,864, 6,897, 6,930, 6,963, 6,996, 7,029, 7,062, 7,095, 7,128, 7,161, 7,194,
'    7,227, 7,260, 7,293, 7,326, 7,359, 7,392, 7,425, 7,458, 7,491, 7,524, 7,557,
'    7,590, 7,623, 7,656, 7,689, 7,722, 7,755, 7,788, 7,821, 7,854, 7,887, 7,920,
'    7,953, 7,986, 8,019, 8,052, 8,085, 8,118, 8,151, 8,184, 8,217, 8,250, 8,283,
'    8,316, 8,349, 8,382, 8,415, 8,448, 8,481, 8,514, 8,547, 8,580, 8,613, 8,646,
'    8,679, 8,712, 8,745, 8,778, 8,811, 8,844, 8,877, 8,910, 8,943, 8,976, 9,009,
'    9,042, 9,075, 9,108, 9,141, 9,174, 9,207, 9,240, 9,273, 9,306, 9,339, 9,372,
'    9,405, 9,438, 9,471, 9,504, 9,537, 9,570, 9,603, 9,636, 9,669, 9,702, 9,735,
'    9,768, 9,801, 9,834, 9,867, 9,900, 9,933, 9,966, 9,999, 10,032, 10,065, 10,098,
'    10,131, 10,164, 10,197, 10,230, 10,263, 10,296, 10,329, 10,362, 10,395, 10,428,
'    10,461, 10,494, 10,527, 10,560, 10,593, 10,626, 10,659, 10,692, 10,725, 10,758,
'    10,791, 10,824, 10,857, 10,890, 10,923, 10,956, 10,989, 11,022, 11,055, 11,088,
'    11,121, 11,154, 11,187, 11,220, 11,253, 11,286, 11,319, 11,352, 11,385, 11,418,
'    11,451, 11,484, 11,517, 11,550, 11,583, 11,616, 11,649, 11,682, 11,715, 11,748,
'    11,781, 11,814, 11,847, 11,880, 11,913, 11,946, 11,979, 12,012, 12,045, 12,078,
'    12,111, 12,144, 12,177, 12,210, 12,243, 12,276, 12,309, 12,342, 12,375, 12,408,
'    12,441, 12,474, 12,507, 12,540, 12,573, 12,606, 12,639, 12,672, 12,705, 12,738,
'    12,771, 12,804, 12,837, 12,870, 12,903, 12,936, 12,969, 13,002, 13,035, 13,068,
'    13,101, 13,134, 13,167, 13,200, 13,233, 13,266,
'    Cancellation requested in continuation...
'
'
'    Cancellation request issued...
'
'    TaskCanceledException: A task was canceled.
'
'    Antecedent Status: RanToCompletion
'    Continuation Status: Canceled

È anche possibile impedire l'esecuzione di una continuazione se la relativa attività precedente viene annullata senza fornire alla continuazione un token di annullamento, specificando l'opzione TaskContinuationOptions.NotOnCanceled quando si crea la continuazione.You can also prevent a continuation from executing if its antecedent is canceled without supplying the continuation a cancellation token by specifying the TaskContinuationOptions.NotOnCanceled option when you create the continuation. Di seguito è riportato un semplice esempio.The following is a simple example.

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

public class Example
{
   public static void Main()
   {
      var cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;
      cts.Cancel();

      var t = Task.FromCanceled(token);
      var continuation = t.ContinueWith( (antecedent) => {
                                            Console.WriteLine("The continuation is running.");
                                          } , TaskContinuationOptions.NotOnCanceled);
      try {
         t.Wait();
      }
      catch (AggregateException ae) {
         foreach (var ie in ae.InnerExceptions)
            Console.WriteLine("{0}: {1}", ie.GetType().Name, ie.Message);

         Console.WriteLine();
      }
      finally {
         cts.Dispose();
      }

      Console.WriteLine("Task {0}: {1:G}", t.Id, t.Status);
      Console.WriteLine("Task {0}: {1:G}", continuation.Id,
                        continuation.Status);
   }
}
// The example displays the following output:
//       TaskCanceledException: A task was canceled.
//
//       Task 1: Canceled
//       Task 2: Canceled
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim cts As New CancellationTokenSource()
      Dim token As CancellationToken = cts.Token
      cts.Cancel()

      Dim t As  Task = Task.FromCanceled(token)
      Dim continuation As Task = t.ContinueWith(Sub(antecedent)
                                                   Console.WriteLine("The continuation is running.")
                                                End Sub, TaskContinuationOptions.NotOnCanceled)
      Try
         t.Wait()
      Catch e As AggregateException
         For Each ie In e.InnerExceptions
            Console.WriteLine("{0}: {1}", ie.GetType().Name, ie.Message)
         Next
         Console.WriteLine()
      Finally
         cts.Dispose()
      End Try

      Console.WriteLine("Task {0}: {1:G}", t.Id, t.Status)
      Console.WriteLine("Task {0}: {1:G}", continuation.Id,
                        continuation.Status)
   End Sub
End Module
' The example displays the following output:
'       TaskCanceledException: A task was canceled.
'
'       Task 1: Canceled
'       Task 2: Canceled

Dopo che una continuazione passa allo stato Canceled , può influire sulle continuazioni successive, a seconda degli oggetti TaskContinuationOptions specificati per tali continuazioni.After a continuation goes into the Canceled state, it may affect continuations that follow, depending on the TaskContinuationOptions that were specified for those continuations.

Le continuazioni eliminate non verranno avviate.Continuations that are disposed will not start.

Continuazioni e attività figlioContinuations and Child Tasks

Una continuazione non viene eseguita fino al completamento dell'attività precedente e di tutte le attività figlio collegate.A continuation does not run until the antecedent and all of its attached child tasks have completed. La continuazione non attende il completamento delle attività figlio scollegate.The continuation does not wait for detached child tasks to finish. I due esempi seguenti mostrano attività figlio collegate e scollegate da un'attività precedente che crea una continuazione.The following two examples illustrate child tasks that are attached to and detached from an antecedent that creates a continuation. Nell'esempio seguente la continuazione viene eseguita solo dopo il completamento di tutte le attività figlio e se l'esempio viene eseguito più volte, produce lo stesso output ogni volta.In the following example, the continuation runs only after all child tasks have completed, and running the example multiple times produces identical output each time. Nell'esempio viene avviata l'attività precedente chiamando il metodo TaskFactory.StartNew, perché per impostazione predefinita il metodo Task.Run crea un'attività padre la cui opzione di creazione di attività predefinita è TaskCreationOptions.DenyChildAttach.The example launches the antecedent by calling the TaskFactory.StartNew method, since by default the Task.Run method creates a parent task whose default task creation option is TaskCreationOptions.DenyChildAttach.

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

public class Example
{
   public static void Main()
   {
      var t = Task.Factory.StartNew( () => { Console.WriteLine("Running antecedent task {0}...",
                                                  Task.CurrentId);
                                             Console.WriteLine("Launching attached child tasks...");
                                             for (int ctr = 1; ctr <= 5; ctr++)  {
                                                int index = ctr;
                                                Task.Factory.StartNew( (value) => {
                                                                       Console.WriteLine("   Attached child task #{0} running",
                                                                                         value);
                                                                       Thread.Sleep(1000);
                                                                     }, index, TaskCreationOptions.AttachedToParent);
                                             }
                                             Console.WriteLine("Finished launching attached child tasks...");
                                           });
      var continuation = t.ContinueWith( (antecedent) => { Console.WriteLine("Executing continuation of Task {0}",
                                                                             antecedent.Id);
                                                         });
      continuation.Wait();
   }
}
// The example displays the following output:
//       Running antecedent task 1...
//       Launching attached child tasks...
//       Finished launching attached child tasks...
//          Attached child task #5 running
//          Attached child task #1 running
//          Attached child task #2 running
//          Attached child task #3 running
//          Attached child task #4 running
//       Executing continuation of Task 1
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Public Module Example
   Public Sub Main()
      Dim t = Task.Factory.StartNew( Sub()
                                        Console.WriteLine("Running antecedent task {0}...",
                                                          Task.CurrentId)
                                        Console.WriteLine("Launching attached child tasks...")
                                        For ctr As Integer = 1 To 5
                                           Dim index As Integer = ctr
                                           Task.Factory.StartNew( Sub(value)
                                                                     Console.WriteLine("   Attached child task #{0} running",
                                                                                       value)
                                                                     Thread.Sleep(1000)
                                                                  End Sub, index, TaskCreationOptions.AttachedToParent)
                                        Next
                                        Console.WriteLine("Finished launching attached child tasks...")
                                     End Sub)
      Dim continuation = t.ContinueWith( Sub(antecedent)
                                            Console.WriteLine("Executing continuation of Task {0}",
                                                              antecedent.Id)
                                         End Sub)
      continuation.Wait()
   End Sub
End Module
' The example displays the following output:
'       Running antecedent task 1...
'       Launching attached child tasks...
'       Finished launching attached child tasks...
'          Attached child task #5 running
'          Attached child task #1 running
'          Attached child task #2 running
'          Attached child task #3 running
'          Attached child task #4 running
'       Executing continuation of Task 1

Se le attività figlio sono scollegate dall'attività precedente, tuttavia, la continuazione viene eseguita non appena termina l'attività precedente, indipendentemente dallo stato delle attività figlio.If child tasks are detached from the antecedent, however, the continuation runs as soon as the antecedent has terminated, regardless of the state of the child tasks. Di conseguenza, se l'esempio precedente viene eseguito più volte, può produrre output variabile che dipende dal modo in cui il l'utilità di pianificazione delle attività ha gestito ogni singola attività figlio.As a result, multiple runs of the following example can produce variable output that depends on how the task scheduler handled each child task.

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

public class Example
{
   public static void Main()
   {
      var t = Task.Factory.StartNew( () => { Console.WriteLine("Running antecedent task {0}...",
                                                  Task.CurrentId);
                                             Console.WriteLine("Launching attached child tasks...");
                                             for (int ctr = 1; ctr <= 5; ctr++)  {
                                                int index = ctr;
                                                Task.Factory.StartNew( (value) => {
                                                                       Console.WriteLine("   Attached child task #{0} running",
                                                                                         value);
                                                                       Thread.Sleep(1000);
                                                                     }, index);
                                             }
                                             Console.WriteLine("Finished launching detached child tasks...");
                                           }, TaskCreationOptions.DenyChildAttach);
      var continuation = t.ContinueWith( (antecedent) => { Console.WriteLine("Executing continuation of Task {0}",
                                                                             antecedent.Id);
                                                         });
      continuation.Wait();
   }
}
// The example displays output like the following:
//       Running antecedent task 1...
//       Launching attached child tasks...
//       Finished launching detached child tasks...
//          Attached child task #1 running
//          Attached child task #2 running
//          Attached child task #5 running
//          Attached child task #3 running
//       Executing continuation of Task 1
//          Attached child task #4 running
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Public Module Example
   Public Sub Main()
      Dim t = Task.Factory.StartNew( Sub()
                                        Console.WriteLine("Running antecedent task {0}...",
                                                          Task.CurrentId)
                                        Console.WriteLine("Launching attached child tasks...")
                                        For ctr As Integer = 1 To 5
                                           Dim index As Integer = ctr
                                           Task.Factory.StartNew( Sub(value)
                                                                     Console.WriteLine("   Attached child task #{0} running",
                                                                                       value)
                                                                     Thread.Sleep(1000)
                                                                  End Sub, index)
                                        Next
                                        Console.WriteLine("Finished launching detached child tasks...")
                                     End Sub, TaskCreationOptions.DenyChildAttach)
      Dim continuation = t.ContinueWith( Sub(antecedent)
                                            Console.WriteLine("Executing continuation of Task {0}",
                                                              antecedent.Id)
                                         End Sub)
      continuation.Wait()
   End Sub
End Module
' The example displays output like the following:
'       Running antecedent task 1...
'       Launching attached child tasks...
'       Finished launching detached child tasks...
'          Attached child task #1 running
'          Attached child task #2 running
'          Attached child task #5 running
'          Attached child task #3 running
'       Executing continuation of Task 1
'          Attached child task #4 running

Lo stato finale dell'attività precedente dipende dallo stato finale di tutte le attività figlio scollegate.The final status of the antecedent task depends on the final status of any attached child tasks. Lo stato delle attività figlio scollegate non influisce sull'attività padre.The status of detached child tasks does not affect the parent. Per altre informazioni, vedere Attached and Detached Child Tasks (Attività figlio connesse e disconnesse).For more information, see Attached and Detached Child Tasks.

Associazione dello stato alle continuazioniAssociating State with Continuations

È possibile associare uno stato arbitrario a una continuazione di attività.You can associate arbitrary state with a task continuation. Il metodo ContinueWith fornisce versioni di overload, ciascuna delle quali può accettare un valore Object che rappresenta lo stato della continuazione.The ContinueWith method provides overloaded versions that each take an Object value that represents the state of the continuation. Successivamente, è possibile accedere a questo oggetto stato usando la proprietà Task.AsyncState .You can later access this state object by using the Task.AsyncState property. Questo oggetto stato è null se non si specifica un valore.This state object is null if you do not provide a value.

Lo stato della continuazione è utile quando si converte codice esistente che usa il modello di programmazione asincrono (APM) per usare TPL.Continuation state is useful when you convert existing code that uses the Asynchronous Programming Model (APM) to use the TPL. Nel modello APM l'oggetto stato viene specificato in genere nel metodo BeginMetodo. In seguito è possibile accedere a tale stato usando la proprietà IAsyncResult.AsyncState.In the APM, you typically provide object state in the BeginMethod method and later access that state by using the IAsyncResult.AsyncState property. Tramite il metodo ContinueWith è possibile mantenere questo stato quando si converte il codice che usa il modello APM per usare TPL.By using the ContinueWith method, you can preserve this state when you convert code that uses the APM to use the TPL.

Lo stato della continuazione può essere utile anche quando si usano oggetti Task nel debugger di Visual Studio.Continuation state can also be useful when you work with Task objects in the Visual Studio debugger. Ad esempio, nella finestra Attività in parallelo la colonna Attività mostra la rappresentazione di stringa dell'oggetto stato per ogni attività.For example, in the Parallel Tasks window, the Task column displays the string representation of the state object for each task. Per altre informazioni sulla finestra Attività in parallelo, vedere Uso della finestra Attività.For more information about the Parallel Tasks window, see Using the Tasks Window.

L'esempio seguente mostra come usare lo stato della continuazione.The following example shows how to use continuation state. Viene creata una catena di attività di continuazione.It creates a chain of continuation tasks. Ogni attività indica la data e l'ora correnti, tramite un oggetto DateTime , per il parametro state del metodo ContinueWith .Each task provides the current time, a DateTime object, for the state parameter of the ContinueWith method. Ogni oggetto DateTime rappresenta la data e l'ora in cui è stata creata l'attività di continuazione.Each DateTime object represents the time at which the continuation task is created. Ogni attività produce come risultato un secondo oggetto DateTime che rappresenta la data e l'ora di fine dell'attività.Each task produces as its result a second DateTime object that represents the time at which the task finishes. Al termine di tutte le attività, questo esempio visualizza la data e l'ora di creazione e la data e l'ora di fine di ogni attività di continuazione.After all tasks finish, this example displays the creation time and the time at which each continuation task finishes.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

// Demonstrates how to associate state with task continuations.
class ContinuationState
{
   // Simluates a lengthy operation and returns the time at which
   // the operation completed.
   public static DateTime DoWork()
   {
      // Simulate work by suspending the current thread 
      // for two seconds.
      Thread.Sleep(2000);

      // Return the current time.
      return DateTime.Now;
   }

   static void Main(string[] args)
   {
      // Start a root task that performs work.
      Task<DateTime> t = Task<DateTime>.Run(delegate { return DoWork(); });

      // Create a chain of continuation tasks, where each task is 
      // followed by another task that performs work.
      List<Task<DateTime>> continuations = new List<Task<DateTime>>();
      for (int i = 0; i < 5; i++)
      {
         // Provide the current time as the state of the continuation.
         t = t.ContinueWith(delegate { return DoWork(); }, DateTime.Now);
         continuations.Add(t);
      }

      // Wait for the last task in the chain to complete.
      t.Wait();

      // Print the creation time of each continuation (the state object)
      // and the completion time (the result of that task) to the console.
      foreach (var continuation in continuations)
      {
         DateTime start = (DateTime)continuation.AsyncState;
         DateTime end = continuation.Result;

         Console.WriteLine("Task was created at {0} and finished at {1}.",
            start.TimeOfDay, end.TimeOfDay);
      }
   }
}

/* Sample output:
Task was created at 10:56:21.1561762 and finished at 10:56:25.1672062.
Task was created at 10:56:21.1610677 and finished at 10:56:27.1707646.
Task was created at 10:56:21.1610677 and finished at 10:56:29.1743230.
Task was created at 10:56:21.1610677 and finished at 10:56:31.1779883.
Task was created at 10:56:21.1610677 and finished at 10:56:33.1837083.
*/
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

' Demonstrates how to associate state with task continuations.
Public Module ContinuationState
   ' Simluates a lengthy operation and returns the time at which
   ' the operation completed.
   Public Function DoWork() As Date
      ' Simulate work by suspending the current thread 
      ' for two seconds.
      Thread.Sleep(2000)

      ' Return the current time.
      Return Date.Now
   End Function

   Public Sub Main()
      ' Start a root task that performs work.
      Dim t As Task(Of Date) = Task(Of Date).Run(Function() DoWork())

      ' Create a chain of continuation tasks, where each task is
      ' followed by another task that performs work.
      Dim continuations As New List(Of Task(Of DateTime))()
      For i As Integer = 0 To 4
         ' Provide the current time as the state of the continuation.
         t = t.ContinueWith(Function(antecedent, state) DoWork(), DateTime.Now)
         continuations.Add(t)
      Next

      ' Wait for the last task in the chain to complete.
      t.Wait()

      ' Display the creation time of each continuation (the state object)
      ' and the completion time (the result of that task) to the console.
      For Each continuation In continuations
         Dim start As DateTime = CDate(continuation.AsyncState)
         Dim [end] As DateTime = continuation.Result

         Console.WriteLine("Task was created at {0} and finished at {1}.",
            start.TimeOfDay, [end].TimeOfDay)
      Next
   End Sub
End Module
' The example displays output like the following:
'       Task was created at 10:56:21.1561762 and finished at 10:56:25.1672062.
'       Task was created at 10:56:21.1610677 and finished at 10:56:27.1707646.
'       Task was created at 10:56:21.1610677 and finished at 10:56:29.1743230.
'       Task was created at 10:56:21.1610677 and finished at 10:56:31.1779883.
'       Task was created at 10:56:21.1610677 and finished at 10:56:33.1837083.

Gestione delle eccezioni generate dalle continuazioniHandling Exceptions Thrown from Continuations

Una relazione tra attività precedenti e attività di continuazione non è una relazione padre-figlio.An antecedent-continuation relationship is not a parent-child relationship. Le eccezioni generate dalle continuazioni non vengono propagate all'attività precedente.Exceptions thrown by continuations are not propagated to the antecedent. Di conseguenza, gestire le eccezioni generate dalle continuazioni allo stesso modo in cui si gestisce qualsiasi altra attività, nel modo seguente:Therefore, handle exceptions thrown by continuations as you would handle them in any other task, as follows:

  • È possibile usare il metodo Wait, WaitAllo WaitAny , o la controparte generica, per restare in attesa della continuazione.You can use the Wait, WaitAll, or WaitAny method, or its generic counterpart, to wait on the continuation. È possibile attendere un'attività precedente e la sua continuazione nella stessa istruzione try , come mostrato nell'esempio seguente.You can wait for an antecedent and its continuations in the same try statement, as shown in the following example.

    using System;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static void Main()
       {
          var task1 = Task<int>.Run( () => { Console.WriteLine("Executing task {0}",
                                                               Task.CurrentId);
                                             return 54; });
          var continuation = task1.ContinueWith( (antecedent) =>
                                                 { Console.WriteLine("Executing continuation task {0}",
                                                                     Task.CurrentId);
                                                   Console.WriteLine("Value from antecedent: {0}",
                                                                     antecedent.Result);
                                                   throw new InvalidOperationException();
                                                } );
    
          try {
             task1.Wait();
             continuation.Wait();
          }
          catch (AggregateException ae) {
              foreach (var ex in ae.InnerExceptions)
                  Console.WriteLine(ex.Message);
          }
       }
    }
    // The example displays the following output:
    //       Executing task 1
    //       Executing continuation task 2
    //       Value from antecedent: 54
    //       Operation is not valid due to the current state of the object.
    
    Imports System.Threading.Tasks
    
    Module Example
       Public Sub Main()
          Dim task1 = Task(Of Integer).Run(Function()
                                              Console.WriteLine("Executing task {0}",
                                                                Task.CurrentId)
                                              Return 54
                                           End Function)
          Dim continuation = task1.ContinueWith(Sub(antecedent)
                                                   Console.WriteLine("Executing continuation task {0}",
                                                                     Task.CurrentId)
                                                   Console.WriteLine("Value from antecedent: {0}",
                                                                     antecedent.Result)
                                                   Throw New InvalidOperationException()
                                                End Sub)
    
          Try
               task1.Wait()
               continuation.Wait()
          Catch ae As AggregateException
              For Each ex In ae.InnerExceptions
                  Console.WriteLine(ex.Message)
              Next
          End Try
       End Sub
    End Module
    ' The example displays the following output:
    '       Executing task 1
    '       Executing continuation task 2
    '       Value from antecedent: 54
    '       Operation is not valid due to the current state of the object.
    
  • È possibile usare una seconda continuazione per osservare la proprietà Exception della prima continuazione.You can use a second continuation to observe the Exception property of the first continuation. Nell'esempio seguente un'attività tenta di leggere da un file inesistente.In the following example, a task attempts to read from a non-existent file. La continuazione visualizza quindi informazioni sull'eccezione nell'attività precedente.The continuation then displays information about the exception in the antecedent task.

    using System;
    using System.IO;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static void Main()
       {
          var t = Task.Run( () => { string s = File.ReadAllText(@"C:\NonexistentFile.txt");
                                    return s;
                                  });
    
          var c = t.ContinueWith( (antecedent) =>
                                  { // Get the antecedent's exception information.
                                    foreach (var ex in antecedent.Exception.InnerExceptions) {
                                       if (ex is FileNotFoundException)
                                          Console.WriteLine(ex.Message);
                                    }
                                  }, TaskContinuationOptions.OnlyOnFaulted);
    
          c.Wait();
       }
    }
    // The example displays the following output:
    //        Could not find file 'C:\NonexistentFile.txt'.
    
    Imports System.IO
    Imports System.Threading.Tasks
    
    Module Example
       Public Sub Main()
          Dim t = Task.Run( Function()
                               Dim s As String = File.ReadAllText("C:\NonexistentFile.txt")
                               Return s
                            End Function)
    
          Dim c = t.ContinueWith( Sub(antecedent)
                                     ' Get the antecedent's exception information.
                                     For Each ex In antecedent.Exception.InnerExceptions
                                        If TypeOf ex Is FileNotFoundException
                                           Console.WriteLine(ex.Message)
                                        End If
                                     Next
                                  End Sub, TaskContinuationOptions.OnlyOnFaulted)
    
          c.Wait()
       End Sub
    End Module
    ' The example displays the following output:
    '       Could not find file 'C:\NonexistentFile.txt'.
    

    Poiché è stata usata l'opzione TaskContinuationOptions.OnlyOnFaulted , la continuazione viene eseguita solo se si verifica un'eccezione nell'attività precedente e di conseguenza può presupporre che la proprietà Exception dell'attività precedente non è null.Because it was run with the TaskContinuationOptions.OnlyOnFaulted option, the continuation executes only if an exception occurs in the antecedent, and therefore it can assume that the antecedent's Exception property is not null. Se la continuazione viene eseguita sia se viene generata un'eccezione nell'attività precedente sia in caso contrario, deve verificare se la proprietà Exception dell'attività precedente è non null prima di tentare di gestire l'eccezione, come mostra il frammento di codice seguente.If the continuation executes whether or not an exception is thrown in the antecedent, it would have to check whether the antecedent's Exception property is not null before attempting to handle the exception, as the following code fragment shows.

    // Determine whether an exception occurred.
    if (antecedent.Exception != null) {
       foreach (var ex in antecedent.Exception.InnerExceptions) {
          if (ex is FileNotFoundException)
             Console.WriteLine(ex.Message);
       }
    }
    
    ' Determine whether an exception occurred.
     If antecedent.Exception IsNot Nothing Then
       ' Get the antecedent's exception information.
        For Each ex In antecedent.Exception.InnerExceptions
           If TypeOf ex Is FileNotFoundException
              Console.WriteLine(ex.Message)
           End If
        Next
     End If
    

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

  • Se la continuazione è un'attività figlio collegata creata usando l'opzione TaskContinuationOptions.AttachedToParent , le eccezioni verranno propagate dall'attività padre di nuovo al thread di chiamata, come nel caso di qualsiasi altra attività figlio collegata.If the continuation is an attached child task that was created by using the TaskContinuationOptions.AttachedToParent option, its exceptions will be propagated by the parent back to the calling thread, as is the case in any other attached child. Per altre informazioni, vedere Attached and Detached Child Tasks (Attività figlio connesse e disconnesse).For more information, see Attached and Detached Child Tasks.

Vedere ancheSee also