Tarefas de continuação

Programação assíncrona, é muito comum para uma operação assíncrona, após a conclusão, para invocar uma segunda operação e passar dados para ele. Tradicionalmente, isso foi feito usando os métodos de retorno de chamada. Na biblioteca paralela de tarefas, a mesma funcionalidade é fornecida pelo tarefas de continuação. Uma tarefa de continuação (também conhecida como uma continuação) é uma tarefa assíncrona que é invocada por outra tarefa, o que é conhecida como o antecedent, quando antecedent for concluída.

Continuação é relativamente fácil de usar, mas não obstante muito poderosa e flexível. Por exemplo, você pode:

  • transmitir dados de antecedent a continuação

  • Especifique as condições precisas sob a qual a continuação será invocada ou não chamada

  • Cancelar uma continuação antes que ele inicie ou cooperativamente enquanto ele estiver sendo executado.

  • fornecer dicas sobre como deve ser programada para a continuação.

  • invocar vários continuação da mesma antecedent

  • chamar uma continuação quando todos ou qualquer um dos vários antecedentes concluída

  • continuação da cadeia um após o outro para qualquer comprimento arbitrário

  • Use uma continuação para manipular exceções lançadas por antecedent

Criar continuação usando o Task.ContinueWith método. O exemplo a seguir mostra o padrão básico, (por motivos de clareza, manipulação de exceção é omitida).

' The antecedent task. Can also be created with Task.Factory.StartNew.
Dim taskA As Task(Of DayOfWeek) = New Task(Of DayOfWeek)(Function()
                                                             Return DateTime.Today.DayOfWeek
                                                         End Function)
' The continuation. Its delegate takes the antecedent task
' as an argument and can return a different type.
Dim continuation As Task(Of String) = taskA.ContinueWith(Function(antecedent)
                                                             Return String.Format("Today is {0}", antecedent.Result)
                                                         End Function)
' Start the antecedent.
taskA.Start()

' Use the contuation's result.
Console.WriteLine(continuation.Result)
            // The antecedent task. Can also be created with Task.Factory.StartNew.
            Task<DayOfWeek> taskA = new Task<DayOfWeek>(() => DateTime.Today.DayOfWeek);

            // The continuation. Its delegate takes the antecedent task
            // as an argument and can return a different type.
            Task<string> continuation = taskA.ContinueWith((antecedent) =>
                {
                    return String.Format("Today is {0}.",
                                        antecedent.Result);
                });

            // Start the antecedent.
            taskA.Start();

            // Use the contuation's result.
            Console.WriteLine(continuation.Result);

Você também pode criar uma continuação de multitarefas será executado quando tem concluído a qualquer ou todos de uma matriz de tarefas, conforme mostrado no exemplo a seguir.

Dim task1 As Task(Of Integer) = New Task(Of Integer)(Function()
                                                         ' Do some work...
                                                         Return 34
                                                     End Function)

Dim task2 As Task(Of Integer) = New Task(Of Integer)(Function()
                                                         ' Do some work...
                                                         Return 8
                                                     End Function)

Dim tasks() As Task(Of Integer) = {task1, task2}

Dim continuation = Task.Factory.ContinueWhenAll(tasks, Sub(antecedents)
                                                           Dim answer As Integer = tasks(0).Result + tasks(1).Result
                                                           Console.WriteLine("The answer is {0}", answer)


                                                       End Sub)
task1.Start()
task2.Start()
continuation.Wait()
            Task<int>[] tasks = new Task<int>[2];
            tasks[0] = new Task<int>(() =>
            {
                // Do some work... 
                return 34;
            });

            tasks[1] = new Task<int>(() =>
            {
                // Do some work...
                 return 8;
            });

            var continuation = Task.Factory.ContinueWhenAll(
                            tasks,
                            (antecedents) =>
                            {
                                int answer = tasks[0].Result + tasks[1].Result;
                                Console.WriteLine("The answer is {0}", answer);
                            });

            tasks[0].Start();
            tasks[1].Start();
            continuation.Wait();

É criada uma continuação do WaitingForActivation estado e, portanto, ele só podem ser iniciadas por sua tarefa antecedente. Para chamar Task.Start em uma continuação no código do usuário gera um System.InvalidOperationException.

É uma continuação de um Task e não bloquear o thread no qual ele é iniciado. Use o método de espera para bloquear até que a conclui a tarefa de continuação.

Opções de continuação

Quando você cria uma tarefa única continuação, você pode usar um ContinueWith sobrecarga que utiliza o System.Threading.Tasks.TaskContinuationOptions enumeração para especificar as condições sob as quais a tarefa antecedente é iniciar a continuação. Por exemplo, você pode especificar que a continuação é ser executada somente se antecedent executou a conclusão ou apenas se ele foi concluída em um estado de falha e assim por diante. Se a condição não é verdadeira quando antecedent está pronto para invocar a continuação, transições de continuação diretamente a Canceled estado e não pode ser iniciado depois disso. Se você especificar qualquer um de NotOn ou OnlyOn Opções de continuação de multitarefas, uma exceção será lançada no tempo de execução.

O System.Threading.Tasks.TaskContinuationOptions enumeração também inclui as mesmas opções que o System.Threading.Tasks.TaskCreationOptions enumeração. AttachedToParent, LongRunning, e PreferFairness tem os mesmos significados e os valores em ambos os tipos de enumeração. Essas opções podem ser usadas com a continuação de multitarefas.

A tabela a seguir lista todos os valores em TaskContinuationOptions.

Elemento

Descrição

None

Especifica o comportamento padrão quando não há TaskContinuationOptions são especificados. A continuação será programada quando antecedent é concluída, independentemente do status de antecedent final. Se a tarefa for um filho, ele é criado como uma tarefa aninhada desanexada.

PreferFairness

Especifica que a continuação será programada para que as tarefas agendadas mais cedo será mais prováveis de ser executado mais cedo, e tarefas agendadas posteriormente será mais prováveis de ser executado posteriormente.

LongRunning

Especifica que a continuação será uma operação longa, refinados. Ele fornece uma dica para o System.Threading.Tasks.TaskScheduler esse excesso de assinatura pode ser garantido.

AttachedToParent

Especifica que a continuação, se for uma tarefa de filho é anexada a um pai na hierarquia de tarefas. A continuação é uma tarefa filho somente se o seu antecessor também é uma tarefa filho.

NotOnRanToCompletion

Especifica que não deve ser agendada a continuação se seu antecessor é executado até a conclusão.

NotOnFaulted

Especifica que não deve ser agendada a continuação se seu antecessor emitiu uma exceção não tratada.

NotOnCanceled

Especifica que não deve ser agendada a continuação se seu antecessor foi cancelada.

OnlyOnRanToCompletion

Especifica que somente deve ser agendada a continuação se antecedent executado até a conclusão.

OnlyOnFaulted

Especifica a continuação deve ser agendada somente se o seu antecessor emitiu uma exceção não tratada. Quando você usa o OnlyOnFaulted opção, ela é garantida que o Exception propriedade antecedent é null. Você pode usar essa propriedade para capturar a exceção e ver qual exceção causou a falha da tarefa. Se você não acessar a Exception propriedade, a exceção será continue sem tratamento. Além disso, se você tentar acessar a Result propriedade de uma tarefa que foi cancelada ou falha, uma nova exceção será gerada.

OnlyOnCanceled

Especifica a continuação deve ser agendada somente se o seu antecessor é completada na Canceled estado.

ExecuteSynchronously

Para a continuação do curta execução. Especifica a continuação idealmente deve ser executada no mesmo thread que faz com que o antecedent a transição entre o estado final. Se antecedent já estiver concluída, quando a continuação é criada, o sistema tentará executar a continuação no thread que cria a continuação. Se o antecessor CancellationTokenSource é descartado em um finally bloco (Finally em Visual Basic), uma continuação com essa opção será executado em que finally bloco.

Passando dados para uma continuação

Uma referência antecedent é passada para o representante do usuário de continuação como um argumento. Se antecedent é um System.Threading.Tasks.Task<TResult>, a tarefa foi executada para a conclusão, e a continuação pode acessar o Task<TResult>.Result a propriedade da tarefa. Com uma continuação de multitarefas e o Task.WaitAll , o argumento é a matriz de antecedentes. Quando você usa Task.WaitAny, o argumento é o primeiro antecedent que foram concluídos.

Task<TResult>.Resultbloqueia até que a tarefa foi concluída. No entanto, se a tarefa foi cancelada ou com defeito, em seguida, Result lançará uma exceção quando seu código tenta acessar o proprietário. Você pode evitar esse problema usando o OnlyOnRanToCompletion opção, conforme mostrado no exemplo a seguir.

            Dim aTask = Task(Of Integer).Factory.StartNew(Function()
                                                              Return 54
                                                          End Function)
            Dim bTask = aTask.ContinueWith(Sub(antecedent)
                                               Console.WriteLine("continuation {0}", antecedent.Result)
                                           End Sub,
                                           TaskContinuationOptions.OnlyOnRanToCompletion)

var t = Task<int>.Factory.StartNew(() => 54);

var c = t.ContinueWith((antecedent) =>
{
    Console.WriteLine("continuation {0}", antecedent.Result);
},
    TaskContinuationOptions.OnlyOnRanToCompletion);

Se desejar que a continuação de execução mesmo quando antecedent não foi executada para a conclusão, você deve contra a exceção. Uma abordagem possível é testar o status de antecedent e só tentará acessar Result se o status não for Faulted ou Canceled. Você também pode examinar o Exception a propriedade do antecessor. Para obter mais informações, consulte (Biblioteca paralela de tarefas) de manipulação de exceção.

Cancelando uma continuação

Uma continuação vai para o Canceled estado nesses cenários:

  • Quando lança um OperationCanceledException em resposta a uma solicitação de cancelamento. Assim como com qualquer tarefa, se a exceção contém o mesmo token que foi passado para a continuação, ele será tratado como uma confirmação do cancelamento cooperativo.

  • Quando a continuação foi passada um System.Threading.CancellationToken como um argumento e o IsCancellationRequested é de propriedade do token true (True) antes da execução de continuação. Nesse caso, a continuação não inicia e faz a transição diretamente para o Canceled estado.

  • Quando a continuação é nunca executado porque a condição definida seus TaskContinuationOptions foi não met. Por exemplo, se uma tarefa entra em um Faulted de estado sua continuação que foi criada pelo NotOnFaulted opção farão a transição para o Canceled estado e será executado.

Para evitar uma continuação da execução se seu antecessor for cancelado, especifique o NotOnCanceled opção quando você cria a continuação.

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 ambas as tarefas, conforme mostrado no exemplo a seguir.

Dim someCondition As Boolean = True
Dim cts As New CancellationTokenSource
Dim task1 = New Task(Sub()
                         Dim ct As CancellationToken = cts.Token
                         While someCondition = True
                             ct.ThrowIfCancellationRequested()
                             ' Do the work here...
                             ' ...
                         End While
                     End Sub,
                     cts.Token
                     )

Dim task2 = task1.ContinueWith(Sub(antecedent)
                                   Dim ct As CancellationToken = cts.Token
                                   While someCondition = True
                                       ct.ThrowIfCancellationRequested()
                                       ' Do the work here
                                       ' ...
                                   End While
                               End Sub,
                               cts.Token)
task1.Start()
' ...
' Antecedent and/or continuation will
' respond to this request, depending on when it is made.
cts.Cancel()
Task task = new Task(() =>
{
    CancellationToken ct = cts.Token;
    while (someCondition)
    {
        ct.ThrowIfCancellationRequested();
        // Do the work.
        //...                        
    }
},
    cts.Token
    );

Task task2 = task.ContinueWith((antecedent) =>
{
    CancellationToken ct = cts.Token;

    while (someCondition)
    {
        ct.ThrowIfCancellationRequested();
        // Do the work.
        //...                        
    }
},
    cts.Token);

task.Start();
//...

// Antecedent and/or continuation will 
// respond to this request, depending on when it is made.
cts.Cancel();

Se antecedent não foi cancelado, o token ainda pode ser usado para cancelar a continuação. Se o antecessor foi cancelado, a continuação não será iniciada.

Após uma continuação entra na Canceled de estado, ele pode afetar a continuação a seguir, dependendo do TaskContinuationOptions que foram especificados para essas continuação.

Continuação sejam descartadas não será iniciado.

Continuação e tarefas filho

Uma continuação não é executada até antecedent e todas as suas tarefas filho anexado concluiu. A continuação não espera para tarefas filho desanexado concluir. O status final da tarefa antecedente depende do status final de quaisquer tarefas filho anexado. O status das tarefas de desanexado filho não afeta o pai. Para obter mais informações, consulte Aninhados de tarefas e tarefas filho.

Tratamento de exceções geradas de continuação

Uma relação de continuação do antecessor não é uma relação pai-filho. Exceções geradas pelo continuação não são propagadas para o antecessor. Portanto, manipular exceções lançadas por continuação, como você trataria-los em qualquer outra tarefa, como segue.

  1. Use o Wait, WaitAll, ou WaitAny método, ou a contraparte genérica, esperar a continuação. Você pode esperar um antecessor e sua continuação na mesma try (Try em Visual Basic) instrução, como mostrado no exemplo a seguir.
Dim task1 = Task(Of Integer).Factory.StartNew(Function()
                                                  Return 54
                                              End Function)
Dim continuation = task1.ContinueWith(Sub(antecedent)
                                          Console.WriteLine("continuation {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

Console.WriteLine("Exception handled. Let's move on.")
var t = Task<int>.Factory.StartNew(() => 54);

var c = t.ContinueWith((antecedent) =>
{
    Console.WriteLine("continuation {0}", antecedent.Result);
    throw new InvalidOperationException();
});

try
{
    t.Wait();
    c.Wait();
}

catch (AggregateException ae)
{
    foreach(var e in ae.InnerExceptions)
        Console.WriteLine(e.Message);
}
Console.WriteLine("Exception handled. Let's move on.");
  1. Use uma continuação de segunda para observar o Exception propriedade de continuação do primeiro. Para obter mais informações, consulte (Biblioteca paralela de tarefas) de manipulação de exceção e Como: Manipular exceções lançadas por tarefas.

  2. Se a continuação é uma tarefa filho e foi criada usando o AttachedToParent opção, então suas exceções serão propagados pelo pai de volta para o segmento de chamada, assim como acontece em qualquer outro anexo filho. Para obter mais informações, consulte Aninhados de tarefas e tarefas filho.

Consulte também

Conceitos

Biblioteca paralela de tarefas

Histórico de alterações

Date

History

Motivo

Junho de 2010

Adicionada uma nota sobre o comportamento assíncrono de continuação.

Comentários do cliente.