Encadeando tarefas usando tarefas de continuaçãoChaining tasks using continuation tasks

Na programação assíncrona, é comum uma operação assíncrona, ao concluir, invocar uma segunda operação.In asynchronous programming, it's common for one asynchronous operation, on completion, to invoke a second operation. As continuações permitem que as operações descendente consumam os resultados da primeira operação.Continuations allow decedent operations to consume the results of the first operation. Tradicionalmente, continuações foram feitas usando os métodos de retorno de chamada.Traditionally, continuations have been done by using callback methods. Na Biblioteca de Tarefas Paralelas, a mesma funcionalidade é fornecida pelas tarefas de continuação.In the Task Parallel Library, the same functionality is provided by continuation tasks. Uma tarefa de continuação (também conhecida como uma continuação) é uma tarefa assíncrona que é invocada por outra tarefa, conhecida como Antecedent, quando o Antecedent é concluído.A continuation task (also known just as a continuation) is an asynchronous task that's invoked by another task, known as the antecedent, when the antecedent finishes.

As continuações são relativamente fáceis de usar, mas mesmo assim são poderosas e flexíveis.Continuations are relatively easy to use, but are nevertheless powerful and flexible. Por exemplo, você pode:For example, you can:

  • Passe dados da antecessora para a continuação.Pass data from the antecedent to the continuation.
  • Especifique as condições precisas sob a qual a continuação será invocada ou não invocada.Specify the precise conditions under which the continuation will be invoked or not invoked.
  • Cancele uma continuação antes que ela comece ou cooperativamente enquanto ela estiver em execução.Cancel a continuation either before it starts or cooperatively as it is running.
  • Forneça dicas sobre como a continuação deve ser agendada.Provide hints about how the continuation should be scheduled.
  • Invoque várias continuações da mesma antecessora.Invoke multiple continuations from the same antecedent.
  • Invoque uma continuação quando todas ou qualquer uma das diversas antecessoras for concluída.Invoke one continuation when all or any one of multiple antecedents complete.
  • Encadeie continuações uma após a outra para qualquer comprimento arbitrário.Chain continuations one after another to any arbitrary length.
  • Use uma continuação para manipular exceções lançadas pela antecessora.Use a continuation to handle exceptions thrown by the antecedent.

Sobre continuaçõesAbout continuations

Uma continuação é uma tarefa que é criada no estado WaitingForActivation.A continuation is a task that is created in the WaitingForActivation state. Ela é ativada automaticamente quando sua tarefa ou tarefas antecessoras são concluídas.It is activated automatically when its antecedent task or tasks complete. Chamar Task.Start em uma continuação no código do usuário gera uma exceção System.InvalidOperationException.Calling Task.Start on a continuation in user code throws an System.InvalidOperationException exception.

Uma continuação é um Task e não bloqueia o thread em que é iniciada.A continuation is itself a Task and does not block the thread on which it is started. Chame o método Task.Wait para bloquear até que a tarefa de continuação seja concluída.Call the Task.Wait method to block until the continuation task finishes.

Criar uma continuação para um único antecedenteCreate a continuation for a single antecedent

Você cria uma continuação que é executada quando seu antecessor é concluído chamando o método Task.ContinueWith.You create a continuation that executes when its antecedent has completed by calling the Task.ContinueWith method. O exemplo a seguir mostra o padrão básico (para esclarecer, o tratamento de exceções é omitido).The following example shows the basic pattern (for clarity, exception handling is omitted). Ela executa uma tarefa antecessora, taskA, que retorna um objeto DayOfWeek que indica o nome do dia da semana atual.It executes an antecedent task, taskA, that returns a DayOfWeek object that indicates the name of the current day of the week. Quando a antecessora é concluída, a tarefa de continuação, continuation, recebe a antecessora e exibe uma cadeia de caracteres que inclui seu resultado.When the antecedent completes, the continuation task, continuation, is passed the antecedent and displays a string that includes its result.

Observação

Os exemplos de C# neste artigo usam o modificador async no método Main.The C# samples in this article make use of the async modifier on the Main method. Este recurso está disponível no C# 7.1 e versões posteriores.That feature is available in C# 7.1 and later. Versões anteriores geram CS5001 ao compilar este código de exemplo.Previous versions generate CS5001 when compiling this sample code. Você precisará definir a versão da linguagem de programação para C# 7.1 ou mais recente.You'll need to set the language version to C# 7.1 or newer. Você pode aprender como configurar a versão da linguagem de programação no artigo sobre configuração da versão da linguagem.You can learn how to configure the language version in the article on configure language version.

using System;
using System.Threading.Tasks;

public class SimpleExample
{
    public static async Task Main()
    {
        // Declare, assign, and start the antecedent task.
        Task<DayOfWeek> taskA = Task.Run(() => DateTime.Today.DayOfWeek);

        // Execute the continuation when the antecedent finishes.
        await taskA.ContinueWith(antecedent => Console.WriteLine($"Today is {antecedent.Result}."));
    }
}
// The example displays 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.

Criar uma continuação para vários antecedentesCreate a continuation for multiple antecedents

Você também pode criar uma continuação que será executada quando qualquer uma ou todo um grupo de tarefas tiver sido concluído.You can also create a continuation that will run when any or all of a group of tasks has completed. Para executar uma continuação quando todas as tarefas antecedentes são concluídas, você chama o método estático (Shared no Visual Basic) Task.WhenAll ou o método de instância TaskFactory.ContinueWhenAll.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. Para executar uma continuação quando qualquer uma das tarefas antecedentes é concluída, você chama o método estático (Shared no Visual Basic) Task.WhenAny ou o método de instância TaskFactory.ContinueWhenAny.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.

Chamadas para as Task.WhenAll Task.WhenAny sobrecargas e não bloqueiam o thread de chamada.Calls to the Task.WhenAll and Task.WhenAny overloads do not block the calling thread. No entanto, normalmente você chama todos Task.WhenAll(IEnumerable<Task>) os Task.WhenAll(Task[]) métodos e, para recuperar a Task<TResult>.Result propriedade retornada, que bloqueia o thread de chamada.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.

O exemplo a seguir chama o método Task.WhenAll(IEnumerable<Task>) para criar uma tarefa de continuação que reflete os resultados das dez tarefas antecedentes.The following example calls the Task.WhenAll(IEnumerable<Task>) method to create a continuation task that reflects the results of its 10 antecedent tasks. Cada tarefa antecessora eleva ao quadrado um valor de índice que varia de um a dez.Each antecedent task squares an index value that ranges from one to 10. Se os antecedentes tiverem sido concluídos com êxito (se sua propriedade Task.Status for TaskStatus.RanToCompletion), a propriedade Task<TResult>.Result da continuação será uma matriz dos valores Task<TResult>.Result retornados por cada antecessor.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. O exemplo os adiciona para calcular a soma dos quadrados de todos os números entre um e dez.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 WhenAllExample
{
    public static async Task Main()
    {
        var tasks = new List<Task<int>>();
        for (int ctr = 1; ctr <= 10; ctr++)
        {
            int baseValue = ctr;
            tasks.Add(Task.Factory.StartNew(b => (int)b * (int)b, baseValue));
        }

        var results = await Task.WhenAll(tasks);

        int sum = 0;
        for (int ctr = 0; ctr <= results.Length - 1; ctr++)
        {
            var result = results[ctr];
            Console.Write($"{result} {((ctr == results.Length - 1) ? "=" : "+")} ");
            sum += result;
        }

        Console.WriteLine(sum);
    }
}
// The example displays the similar 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

Opções de continuaçãoContinuation options

Ao criar uma continuação de tarefa única, você pode usar uma sobrecarga ContinueWith que utiliza um valor de enumeração System.Threading.Tasks.TaskContinuationOptions para especificar as condições sob as quais a continuação é iniciada.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. Por exemplo, você pode especificar que a continuação só será executada se a antecessora for concluída com êxito ou apenas se ela for concluída em um estado de falha.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 a condição não for verdadeira quando a antecessora estiver pronta para invocar a continuação, a continuação fará a transição diretamente para o estado TaskStatus.Canceled e não poderá ser iniciada depois disso.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.

Vários métodos de continuação de várias tarefas, como sobrecargas do método TaskFactory.ContinueWhenAll, também incluem um parâmetro 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. Somente um subconjunto de todos os membros de enumeração System.Threading.Tasks.TaskContinuationOptions é válido, no entanto.Only a subset of all System.Threading.Tasks.TaskContinuationOptions enumeration members are valid, however. Você pode especificar valores System.Threading.Tasks.TaskContinuationOptions que têm contrapartes na enumeração System.Threading.Tasks.TaskCreationOptions, como TaskContinuationOptions.AttachedToParent, TaskContinuationOptions.LongRunning e 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 você especificar qualquer uma das opções NotOn ou OnlyOn com uma continuação de várias tarefas, uma exceção ArgumentOutOfRangeException será gerada em tempo de execução.If you specify any of the NotOn or OnlyOn options with a multi-task continuation, an ArgumentOutOfRangeException exception will be thrown at run time.

Para saber mais sobre opções de continuação de tarefas, confira o tópico TaskContinuationOptions.For more information on task continuation options, see the TaskContinuationOptions topic.

Passar dados para uma continuaçãoPass data to a continuation

O método Task.ContinueWith passa uma referência ao antecessor para o representante de usuário de continuação como um argumento.The Task.ContinueWith method passes a reference to the antecedent to the user delegate of the continuation as an argument. Se o antecessor for um objeto System.Threading.Tasks.Task<TResult>, e a tarefa tiver sido executada ser concluída, a continuação poderá acessar a propriedade Task<TResult>.Result da tarefa.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.

A propriedade Task<TResult>.Result realiza o bloqueio até que a tarefa seja concluída.The Task<TResult>.Result property blocks until the task has completed. No entanto, se a tarefa foi cancelada ou falhou, a tentativa de acessar a propriedade Result gera uma exceção AggregateException.However, if the task was canceled or faulted, attempting to access the Result property throws an AggregateException exception. Você pode evitar esse problema usando a opção OnlyOnRanToCompletion, conforme mostrado no exemplo a seguir.You can avoid this problem by using the OnlyOnRanToCompletion option, as shown in the following example.

using System;
using System.Threading.Tasks;

public class ResultExample
{
    public static async Task Main()
    {
       var task = Task.Run(
           () =>
           {
                DateTime date = DateTime.Now;
                return date.Hour > 17
                    ? "evening"
                    : date.Hour > 12
                        ? "afternoon"
                        : "morning";
            });
        
        await task.ContinueWith(
            antecedent =>
            {
                Console.WriteLine($"Good {antecedent.Result}!");
                Console.WriteLine($"And how are you this fine {antecedent.Result}?");
            }, TaskContinuationOptions.OnlyOnRanToCompletion);
   }
}
// The example displays the similar output:
//       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 você quiser que a continuação seja executada mesmo se a antecessora não tiver sido concluída com êxito, deverá se proteger contra a exceção.If you want the continuation to run even if the antecedent did not run to successful completion, you must guard against the exception. Uma abordagem é testar a propriedade Task.Status do antecedente e só tentar acessar a propriedade Result se o status não é Faulted ou 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. Você também pode examinar a propriedade Exception do antecessor.You can also examine the Exception property of the antecedent. Para saber mais, veja Tratamento de exceção.For more information, see Exception Handling. O exemplo a seguir modifica o exemplo anterior para acessar a propriedade Task<TResult>.Result do antecessor somente se o status é 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 ResultTwoExample
{
    public static async Task Main() =>
        await Task.Run(
            () =>
            {
                DateTime date = DateTime.Now;
                return date.Hour > 17
                   ? "evening"
                   : date.Hour > 12
                       ? "afternoon"
                       : "morning";
            })
            .ContinueWith(
                antecedent =>
                {
                    if (antecedent.Status == TaskStatus.RanToCompletion)
                    {
                        Console.WriteLine($"Good {antecedent.Result}!");
                        Console.WriteLine($"And how are you this fine {antecedent.Result}?");
                    }
                    else if (antecedent.Status == TaskStatus.Faulted)
                    {
                        Console.WriteLine(antecedent.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?

Cancelar uma continuaçãoCancel a continuation

A propriedade Task.Status de uma continuação é definida como TaskStatus.Canceled nas seguintes situações:The Task.Status property of a continuation is set to TaskStatus.Canceled in the following situations:

Se uma tarefa e sua continuação representam duas partes da mesma operação lógica, você pode passar o mesmo token de cancelamento para as duas tarefas, conforme mostrado no exemplo a seguir.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. Ele consiste em uma antecessora que gera uma lista de inteiros divisíveis por 33, que é passada para a continuação.It consists of an antecedent that generates a list of integers that are divisible by 33, which it passes to the continuation. Por sua vez, a continuação exibe a lista.The continuation in turn displays the list. A antecessora e a continuação pausam regularmente por intervalos aleatórios.Both the antecedent and the continuation pause regularly for random intervals. Além disso, um objeto System.Threading.Timer é usado para executar o método Elapsed após um intervalo de tempo limite de cinco segundos.In addition, a System.Threading.Timer object is used to execute the Elapsed method after a five-second timeout interval. Este exemplo chama o método CancellationTokenSource.Cancel, o que faz com que a tarefa em execução no momento chame o método CancellationToken.ThrowIfCancellationRequested.This example calls the CancellationTokenSource.Cancel method, which causes the currently executing task to call the CancellationToken.ThrowIfCancellationRequested method. O método CancellationTokenSource.Cancel é chamado ou não quando o antecessor ou sua continuação está em execução, dependendo da duração das pausas geradas aleatoriamente.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 a antecessora for cancelada, a continuação não será iniciada.If the antecedent is canceled, the continuation will not start. Se a antecessora não for cancelada, o token ainda poderá ser usado para cancelar a continuação.If the antecedent is not canceled, the token can still be used to cancel the continuation.

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

public class CancellationExample
{
    static readonly Random s_random = new Random((int)DateTime.Now.Ticks);

    public static async Task Main()
    {
        using var cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;
        var timer = new Timer(Elapsed, cts, 5000, Timeout.Infinite);

        var task = Task.Run(
            async () =>
            {
                var product33 = new List<int>();
                for (int index = 1; index < short.MaxValue; index++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("\nCancellation requested in antecedent...\n");
                        token.ThrowIfCancellationRequested();
                    }
                    if (index % 2000 == 0)
                    {
                        int delay = s_random.Next(16, 501);
                        await Task.Delay(delay);
                    }
                    if (index % 33 == 0)
                    {
                        product33.Add(index);
                    }
                }

                return product33.ToArray();
            }, token);

        Task<double> continuation = task.ContinueWith(
            async antecedent =>
            {
                Console.WriteLine("Multiples of 33:\n");
                int[] array = antecedent.Result;
                for (int index = 0; index < array.Length; index++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("\nCancellation requested in continuation...\n");
                        token.ThrowIfCancellationRequested();
                    }
                    if (index % 100 == 0)
                    {
                        int delay = s_random.Next(16, 251);
                        await Task.Delay(delay);
                    }

                    Console.Write($"{array[index]:N0}{(index != array.Length - 1 ? ", " : "")}");

                    if (Console.CursorLeft >= 74)
                    {
                        Console.WriteLine();
                    }
                }
                Console.WriteLine();
                return array.Average();
            }, token).Unwrap();

        try
        {
            await task;
            double result = await continuation;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

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

    static void Elapsed(object state)
    {
        if (state is CancellationTokenSource cts)
        {
            cts.Cancel();
            Console.WriteLine("\nCancellation request issued...\n");
        }
    }
}
// The example displays the similar 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,
//     Cancellation request issued...
//
//     5,775,
//     Cancellation requested in continuation...
//       
//     The operation 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

Você também pode impedir a execução de uma continuação caso sua antecessora seja cancelada sem o fornecimento da continuação de um token de cancelamento especificando a opção TaskContinuationOptions.NotOnCanceled quando a continuação for criada.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. A seguir há um exemplo simples.The following is a simple example.

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

public class CancellationTwoExample
{
    public static async Task Main()
    {
        using var cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;
        cts.Cancel();

        var task = Task.FromCanceled(token);
        Task continuation =
            task.ContinueWith(
                antecedent => Console.WriteLine("The continuation is running."),
                TaskContinuationOptions.NotOnCanceled);

        try
        {
            await task;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
            Console.WriteLine();
        }

        Console.WriteLine($"Task {task.Id}: {task.Status:G}");
        Console.WriteLine($"Task {continuation.Id}: {continuation.Status:G}");
    }
}
// The example displays the similar 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

Depois que uma continuação entra no estado Canceled, pode afetar as continuações seguintes, dependendo do TaskContinuationOptions especificado para as continuações.After a continuation goes into the Canceled state, it may affect continuations that follow, depending on the TaskContinuationOptions that were specified for those continuations.

As continuações descartadas não serão iniciadas.Continuations that are disposed will not start.

Tarefas de continuação e filhoContinuations and child tasks

Uma continuação não é executada até que a antecessora e todas suas tarefas filhas anexadas sejam concluídas.A continuation does not run until the antecedent and all of its attached child tasks have completed. A continuação não espera a conclusão de tarefas filhas desanexadas.The continuation does not wait for detached child tasks to finish. Os dois exemplos a seguir ilustram tarefas filhas anexadas e desanexadas de uma antecessora que cria uma continuação.The following two examples illustrate child tasks that are attached to and detached from an antecedent that creates a continuation. No exemplo a seguir, a continuação é executada somente após a conclusão de todas as tarefas filhas e executar o exemplo várias vezes produzirá uma saída idêntica em todas elas.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. O exemplo inicia o antecessor chamando o método TaskFactory.StartNew, pois, por padrão, o método Task.Run cria uma tarefa pai de opção de criação de tarefa cujo padrão é 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.Tasks;

public class AttachedExample
{
    public static async Task Main()
    {
        await Task.Factory
                  .StartNew(
            () =>
            {
                Console.WriteLine($"Running antecedent task {Task.CurrentId}...");
                Console.WriteLine("Launching attached child tasks...");
                for (int ctr = 1; ctr <= 5; ctr++)
                {
                    int index = ctr;
                    Task.Factory.StartNew(async value =>
                    {
                        Console.WriteLine($"   Attached child task #{value} running");
                        await Task.Delay(1000);
                    }, index, TaskCreationOptions.AttachedToParent);
                }
                Console.WriteLine("Finished launching attached child tasks...");
            }).ContinueWith(
                antecedent =>
                    Console.WriteLine($"Executing continuation of Task {antecedent.Id}"));
    }
}
// The example displays the similar output:
//     Running antecedent task 1...
//     Launching attached child tasks...
//     Finished launching attached child tasks...
//        Attached child task #1 running
//        Attached child task #5 running
//        Attached child task #3 running
//        Attached child task #2 running
//        Attached child task #4 running
//     Executing continuation of Task 1
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 as tarefas filhas forem desanexadas da antecessora, no entanto, a continuação será executada assim que a antecessor tiver sido finalizada, independentemente do estado das tarefas filhas.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. Como resultado, várias execuções do exemplo a seguir podem produzir uma saída variável que depende de como o agendador de tarefas tratou cada tarefa filha.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.Tasks;

public class DetachedExample
{
    public static async Task Main()
    {
        Task task =
            Task.Factory.StartNew(
                () =>
                {
                    Console.WriteLine($"Running antecedent task {Task.CurrentId}...");
                    Console.WriteLine("Launching attached child tasks...");
                    for (int ctr = 1; ctr <= 5; ctr++)
                    {
                        int index = ctr;
                        Task.Factory.StartNew(
                            async value =>
                            {
                                Console.WriteLine($"   Attached child task #{value} running");
                                await Task.Delay(1000);
                            }, index);
                    }
                    Console.WriteLine("Finished launching detached child tasks...");
                }, TaskCreationOptions.DenyChildAttach);

        Task continuation =
            task.ContinueWith(
                antecedent =>
                    Console.WriteLine($"Executing continuation of Task {antecedent.Id}"));

        await continuation;

        Console.ReadLine();
    }
}
// The example displays the similar output:
//     Running antecedent task 1...
//     Launching attached child tasks...
//     Finished launching detached child tasks...
//     Executing continuation of Task 1
//        Attached child task #1 running
//        Attached child task #5 running
//        Attached child task #2 running
//        Attached child task #3 running
//        Attached child task #4 running
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

O status final da tarefa antecessora depende do status final de quaisquer tarefas filhas anexadas.The final status of the antecedent task depends on the final status of any attached child tasks. O status de tarefas filhas desanexadas não afeta a principal.The status of detached child tasks does not affect the parent. Para obter mais informações, consulte Tarefas filho anexadas e desanexadas.For more information, see Attached and Detached Child Tasks.

Associar o estado a continuasAssociate state with continuations

Você pode associar o estado arbitrário a uma continuação da tarefa.You can associate arbitrary state with a task continuation. O método ContinueWith fornece versões sobrecarregadas que utilizam um valor Object que representa o estado de continuação.The ContinueWith method provides overloaded versions that each take an Object value that represents the state of the continuation. Mais tarde, você pode acessar esse objeto de estado usando a propriedade Task.AsyncState.You can later access this state object by using the Task.AsyncState property. Esse objeto de estado será null se você não fornecer um valor.This state object is null if you do not provide a value.

O estado de continuação é útil quando você converte o código existente que usa o APM (Modelo de Programação Assíncrona) para usar o TPL.Continuation state is useful when you convert existing code that uses the Asynchronous Programming Model (APM) to use the TPL. No APM, você normalmente fornece o estado do objeto no método BeginMethod e acessa posteriormente esse estado usando a propriedade 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. Usando o método ContinueWith, você pode preservar esse estado ao converter o código que usa o APM para usar a TPL.By using the ContinueWith method, you can preserve this state when you convert code that uses the APM to use the TPL.

O estado de continuação também pode ser útil quando você trabalha com objetos Task no depurador do Visual Studio.Continuation state can also be useful when you work with Task objects in the Visual Studio debugger. Por exemplo, na janela Tarefas Paralelas, a coluna Tarefa exibe a representação de cadeia de caracteres do objeto de estado para cada tarefa.For example, in the Parallel Tasks window, the Task column displays the string representation of the state object for each task. Para saber mais sobre a janela Tarefas Paralelas, veja Uso da janela Tarefas.For more information about the Parallel Tasks window, see Using the Tasks Window.

O exemplo a seguir mostra como usar o estado de continuação.The following example shows how to use continuation state. Ele cria uma cadeia de tarefas de continuação.It creates a chain of continuation tasks. Cada tarefa fornece a hora atual, um objeto DateTime, para o parâmetro state do método ContinueWith.Each task provides the current time, a DateTime object, for the state parameter of the ContinueWith method. Cada objeto DateTime representa a hora em que a tarefa de continuação é criada.Each DateTime object represents the time at which the continuation task is created. Cada tarefa gera como resultado um segundo objeto DateTime que representa a hora de conclusão da tarefa.Each task produces as its result a second DateTime object that represents the time at which the task finishes. Depois de concluir todas as tarefas, este exemplo exibe a hora de criação e a hora em que cada continuação a tarefa é concluída.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;

class ContinuationStateExample
{
    static DateTime DoWork()
    {
        Thread.Sleep(2000);

        return DateTime.Now;
    }

    static async Task Main()
    {
        Task<DateTime> task = Task.Run(() => DoWork());

        var continuations = new List<Task<DateTime>>();
        for (int i = 0; i < 5; i++)
        {
            task = task.ContinueWith((antecedent, _) => DoWork(), DateTime.Now);
            continuations.Add(task);
        }

        await task;

        foreach (Task<DateTime> continuation in continuations)
        {
            DateTime start = (DateTime)continuation.AsyncState;
            DateTime end = continuation.Result;

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

        Console.ReadLine();
    }
}
// The example displays the similar 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.

Continuas que retornam tipos de tarefasContinuations that return Task types

Às vezes, talvez seja necessário encadear uma continuação que retorne um Task tipo.Sometimes you may need to chain a continuation that returns a Task type. Eles são chamados de tarefas aninhadas e são comuns.These are referred to as nested tasks, and they are common. Quando uma tarefa pai chama Task<TResult>.ContinueWith , e fornece uma continuationFunction tarefa que retorna Unwrap a chamada para criar uma tarefa de proxy que representa a operação assíncrona do <Task<Task<T>>> ou Task(Of Task(Of T)) (Visual Basic).When a parent task calls Task<TResult>.ContinueWith, and provides a continuationFunction that is task returning you call Unwrap to create a proxy task that represents the asynchronous operation of the <Task<Task<T>>> or Task(Of Task(Of T)) (Visual Basic).

O exemplo a seguir mostra como usar as continuações que encapsulam funções de retorno de tarefas adicionais.The following example shows how to use continuations that wrap additional task returning functions. Cada continuação pode ser desencapsulada, expondo a tarefa interna que foi encapsulada.Each continuation can be unwrapped, exposing the inner task that was wrapped.

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

public class UnwrapExample
{
    public static async Task Main()
    {
        Task<int> taskOne = RemoteIncrement(0);
        Console.WriteLine("Started RemoteIncrement(0)");

        Task<int> taskTwo = RemoteIncrement(4)
            .ContinueWith(t => RemoteIncrement(t.Result))
            .Unwrap().ContinueWith(t => RemoteIncrement(t.Result))
            .Unwrap().ContinueWith(t => RemoteIncrement(t.Result))
            .Unwrap();

        Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");

        try
        {
            await taskOne;
            Console.WriteLine("Finished RemoteIncrement(0)");

            await taskTwo;
            Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");
        }
        catch (Exception e)
        {
            Console.WriteLine($"A task has thrown the following (unexpected) exception:\n{e}");
        }
    }

    static Task<int> RemoteIncrement(int number) =>
        Task<int>.Factory.StartNew(
            obj =>
            {
                Thread.Sleep(1000);

                int x = (int)obj;
                Console.WriteLine("Thread={0}, Next={1}", Thread.CurrentThread.ManagedThreadId, ++x);
                return x;
            },
            number);
}

// The example displays the similar output:
//     Started RemoteIncrement(0)
//     Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
//     Thread=4, Next=1
//     Finished RemoteIncrement(0)
//     Thread=5, Next=5
//     Thread=6, Next=6
//     Thread=6, Next=7
//     Thread=6, Next=8
//     Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
Imports System.Threading

Module UnwrapExample
    Sub Main()
        Dim taskOne As Task(Of Integer) = RemoteIncrement(0)
        Console.WriteLine("Started RemoteIncrement(0)")

        Dim taskTwo As Task(Of Integer) = RemoteIncrement(4).
            ContinueWith(Function(t) RemoteIncrement(t.Result)).
            Unwrap().ContinueWith(Function(t) RemoteIncrement(t.Result)).
            Unwrap().ContinueWith(Function(t) RemoteIncrement(t.Result)).
            Unwrap()

        Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")

        Try
            taskOne.Wait()
            Console.WriteLine("Finished RemoteIncrement(0)")

            taskTwo.Wait()
            Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")
        Catch e As AggregateException
            Console.WriteLine($"A task has thrown the following (unexpected) exception:{vbLf}{e}")
        End Try
    End Sub

    Function RemoteIncrement(ByVal number As Integer) As Task(Of Integer)
        Return Task(Of Integer).Factory.StartNew(
            Function(obj)
                Thread.Sleep(1000)

                Dim x As Integer = CInt(obj)
                Console.WriteLine("Thread={0}, Next={1}", Thread.CurrentThread.ManagedThreadId, Interlocked.Increment(x))
                Return x
            End Function, number)
    End Function
End Module

' The example displays the similar output:
'     Started RemoteIncrement(0)
'     Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
'     Thread=4, Next=1
'     Finished RemoteIncrement(0)
'     Thread=5, Next=5
'     Thread=6, Next=6
'     Thread=6, Next=7
'     Thread=6, Next=8
'     Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)

Para obter mais informações sobre Unwrap como usar o, consulte Como: desencapsular uma tarefa aninhada.For more information on using Unwrap, see How to: Unwrap a nested Task.

Tratar exceções geradas a partir de continuaçãosHandle exceptions thrown from continuations

Uma relação antecessora-continuação não é uma relação pai-filho.An antecedent-continuation relationship is not a parent-child relationship. As exceções geradas por continuações não são propagadas para o antecessor.Exceptions thrown by continuations are not propagated to the antecedent. Portanto, trate as exceções geradas por continuações como você lidaria com elas em qualquer outra tarefa, da seguinte maneira:Therefore, handle exceptions thrown by continuations as you would handle them in any other task, as follows:

  • Você pode usar o método Wait, WaitAll ou WaitAny, ou seu equivalente genérico, para aguardar a continuação.You can use the Wait, WaitAll, or WaitAny method, or its generic counterpart, to wait on the continuation. Você pode esperar por uma antecessora e suas continuações na mesma instrução try, conforme mostrado no exemplo a seguir.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 ExceptionExample
{
    public static async Task Main()
    {
        Task<int> task = Task.Run(
            () =>
            {
                Console.WriteLine($"Executing task {Task.CurrentId}");
                return 54;
            });

        var continuation = task.ContinueWith(
            antecedent =>
            {
                Console.WriteLine($"Executing continuation task {Task.CurrentId}");
                Console.WriteLine($"Value from antecedent: {antecedent.Result}");

                throw new InvalidOperationException();
            });

        try
        {
            await task;
            await continuation;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
// The example displays the similar 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.
  • Você pode usar uma segunda continuação para observar a propriedade Exception da primeira continuação.You can use a second continuation to observe the Exception property of the first continuation. No exemplo a seguir, uma tarefa tenta ler um arquivo inexistente.In the following example, a task attempts to read from a non-existent file. Em seguida, a continuação exibe informações sobre a exceção na tarefa antecessora.The continuation then displays information about the exception in the antecedent task.
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

public class ExceptionTwoExample
{
    public static async Task Main()
    {
        var task = Task.Run(
            () =>
            {
                string fileText = File.ReadAllText(@"C:\NonexistentFile.txt");
                return fileText;
            });

        Task continuation = task.ContinueWith(
            antecedent =>
            {
                var fileNotFound =
                    antecedent.Exception
                        ?.InnerExceptions
                        ?.FirstOrDefault(e => e is FileNotFoundException) as FileNotFoundException;

                if (fileNotFound != null)
                {
                    Console.WriteLine(fileNotFound.Message);
                }
            }, TaskContinuationOptions.OnlyOnFaulted);

        await continuation;

        Console.ReadLine();
    }
}
// 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'.

Como ele foi executado com a opção TaskContinuationOptions.OnlyOnFaulted, a continuação será executada somente se uma exceção ocorrer no antecessor. Portanto, ele pode presumir que a propriedade Exception do antecessor não é 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 a continuação for executada, caso uma exceção seja ou não gerada na antecessora, ela deverá verificar se a propriedade Exception da antecessora não é null antes de tentar tratar a exceção, como mostrado pelo fragmento de código a seguir.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.

var fileNotFound =
    antecedent.Exception
        ?.InnerExceptions
        ?.FirstOrDefault(e => e is FileNotFoundException) as FileNotFoundException;

if (fileNotFound != null)
{
    Console.WriteLine(fileNotFound.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

Para saber mais, veja Tratamento de exceção.For more information, see Exception Handling.

Veja tambémSee also