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 |
---|---|
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. |
|
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. |
|
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. |
|
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. |
|
Especifica que não deve ser agendada a continuação se seu antecessor é executado até a conclusão. |
|
Especifica que não deve ser agendada a continuação se seu antecessor emitiu uma exceção não tratada. |
|
Especifica que não deve ser agendada a continuação se seu antecessor foi cancelada. |
|
Especifica que somente deve ser agendada a continuação se antecedent executado até a conclusão. |
|
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. |
|
Especifica a continuação deve ser agendada somente se o seu antecessor é completada na Canceled estado. |
|
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.
- 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.");
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.
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. |