Resumo : neste tutorial, você aprenderá como usar o ContinueWith()
método C# da Task
classe para continuar uma operação assíncrona quando concluída.
Introdução ao método C# ContinueWith()
O programa a seguir demonstra como usar um Task
para executar uma operação demorada em um thread separado e ainda ser capaz de recuperar o resultado dessa operação no thread principal:
static int GetSquareNumber(int number)
{
Thread.Sleep(3000);
return number * number;
}
var result = 0;
var task = Task.Run(() => GetSquareNumber(10));
// block the main thread
result = task.Result;
while (result == 0)
{
Console.WriteLine("Waiting for the result...");
Thread.Sleep(1000);
}
Console.WriteLine(result);
Linguagem de código: C# ( cs )
Como funciona.
Primeiro, defina um método chamado GetSquareNumber()
que receba um número inteiro e retorne o número quadrado desse número após um atraso de três segundos. O método usa Thread.Sleep()
para pausar o thread atual por três segundos.
Em seguida, o programa cria um novo método Task
usando Task.Run()
, que executa o GetSquareNumber()
método com um argumento de 10 em um thread separado. O Task.Run()
retorna um Task
objeto armazenado na task
variável.
Para recuperar o resultado do GetSquareNumber()
método, o programa utiliza task.Result
a propriedade, que bloqueia os threads principais até que a tarefa seja concluída e o resultado esteja disponível.
O código dentro do while
loop não é executado porque a result
variável é zero quando a execução atinge o while
loop.
O motivo é que task.Result
bloqueia o thread principal até que GetSquareNumber()
o método retorne o resultado e o atribua à result
variável.
Para criar uma continuação que será executada de forma assíncrona assim que a tarefa de destino for concluída, você usa o método estático ContinueWith()
da Task
classe.
Por exemplo:
static int GetSquareNumber(int number)
{
Thread.Sleep(3000);
return number * number;
}
// main thread
var result = 0;
// create a new task (in a different thread)
var task = Task.Run(() => GetSquareNumber(10));
// create a continuation
task.ContinueWith((task) =>
{
// a different thread
result = task.Result;
Console.WriteLine($"Result:{result}");
});
while (result == 0)
{
Console.WriteLine("Waiting for the result...");
Thread.Sleep(1000);
}
Linguagem de código: C# ( cs )
Saída:
Waiting for the result...
Waiting for the result...
Waiting for the result...
Result:100
Linguagem de código: texto simples ( texto simples )
Neste exemplo, o thread principal grava uma mensagem no console 3 vezes, cada uma por segundo, até que a tarefa seja concluída.
O ContinueWith()
método a seguir cria uma continuação quando a tarefa é concluída. Ele executa um método que atribui o resultado da tarefa à result
variável e o grava no console.
Encadeamento de tarefas
Se você tiver várias operações assíncronas que precisam ser executadas em uma ordem específica e quiser iniciar uma operação após a conclusão de outra operação, poderá usar uma técnica conhecida como encadeamento de operações assíncronas ou encadeamento de tarefas.
O programa a seguir demonstra como encadear tarefas usando o ContinueWith()
método:
using static System.Console;
var t1 = Task.Run(() => 5);
var t2 = t1.ContinueWith(t => t.Result * 2);
var t3 = t2.ContinueWith(t => t.Result + 10);
t3.ContinueWith(t => WriteLine(t.Result));
// wait for the tasks to complete
Read();
Linguagem de código: C# ( cs )
Como funciona.
Primeiro, crie uma nova Task
chamada t1
que retorne o valor inteiro 5
usando o Task.Run()
método.
A seguir, crie uma nova Task
chamada t2
chamando o ContinueWith()
método em task t1
. A expressão lambda passada ao ContinueWith()
método especifica que o resultado de t1
deve ser multiplicado por 2
.
Em seguida, crie uma nova Task
chamada t3
chamando o ContinueWith()
método on t2
. A expressão lambda especifica que o resultado de t2
deve ser incrementado em 10
.
Depois disso, use o ContinueWith()
método para aguardar t3
a conclusão e grave o resultado final no console usando o WriteLine()
método.
Por fim, use o Read()
método para aguardar a conclusão das tarefas antes de o programa encerrar. Isso garante que a saída seja exibida no console antes do término do programa.
Como os ContinueWith()
retornos a Task
, você pode encadeá-los assim para tornar o código mais conciso:
using static System.Console;
Task.Run(() => 5)
.ContinueWith(t => t.Result * 2)
.ContinueWith(t => t.Result + 10)
.ContinueWith(t => WriteLine(t.Result));
// wait for the tasks to complete
Read();
Linguagem de código: C# ( cs )
Opções de continuação
Às vezes, você deseja criar uma continuação com base no fato de a tarefa ter sido concluída com êxito ou com falha, ou ambos. O método ContinueWith() possui um segundo parâmetro do tipo TaskContinuationOptions que permite fazer isso.
O exemplo a seguir mostra como usar o método ContinueWith com TaskContinuationOptions
using static System.Console;
static int GetRandomNumber(int min, int max)
{
if (min >= max)
{
throw new ArgumentException($"The {min} must less than {max}");
}
Thread.Sleep(1000);
return new Random().Next(min, max);
}
static int GetInt(string message)
{
int n = 0;
while (true)
{
Write(message);
string? input = ReadLine();
if (int.TryParse(input, out n))
{
return n;
}
}
}
int min = GetInt("Enter the min integer:");
int max = GetInt("Enter the max integer:");
var task = Task.Run(() => GetRandomNumber(min, max));
// continue if ran to completion
task.ContinueWith(t =>
{
WriteLine($"Result: {t.Result}");
},
TaskContinuationOptions.OnlyOnRanToCompletion);
// continue if only faulted
task.ContinueWith(t =>
{
WriteLine($"The task completed with the {task.Status} status");
},
TaskContinuationOptions.OnlyOnFaulted);
Read();
Linguagem de código: C# ( cs )
Como funciona.
Primeiro, defina o método GetRandomNumber() que retorna um número aleatório entre mínimo e máximo após um atraso de um segundo. Lança uma exceção se o mínimo for maior que o máximo:
static int GetRandomNumber(int min, int max)
{
if (min >= max)
{
throw new ArgumentException($"The {min} must less than {max}");
}
Thread.Sleep(1000);
return new Random().Next(min, max);
}
Linguagem de código: C# ( cs )
Segundo, defina o método GetInt() que retorna um número inteiro da entrada do usuário:
static int GetInt(string message)
{
int n = 0;
while (true)
{
Write(message);
string? input = ReadLine();
if (int.TryParse(input, out n))
{
return n;
}
}
}
Linguagem de código: C# ( cs )
Terceiro, solicite ao usuário os números inteiros mínimo e máximo:
int min = GetInt("Enter the min integer:");
int max = GetInt("Enter the max integer:");
Linguagem de código: C# ( cs )
Quarto, crie uma tarefa que retorne um número inteiro aleatório entre o mínimo e o máximo:
var task = Task.Run(() => GetRandomNumber(min, max));
Linguagem de código: C# ( cs )
Se o mínimo for menor que o máximo, a tarefa será executada até a conclusão. Caso contrário, será culpado.
Quinto, crie uma continuação se a tarefa for concluída usando o método ContinueWith() com a opção TaskContinuationOptions.OnlyOnRanToCompletion:
task.ContinueWith(t =>
{
WriteLine($"Result: {t.Result}");
},
TaskContinuationOptions.OnlyOnRanToCompletion);
Linguagem de código: C# ( cs )
Quinto, crie uma segunda continuação se a tarefa apresentar falha usando o método ContinueWith() com a opção TaskContinuationOptions.OnlyOnFaulted:
// continue if only faulted
task.ContinueWith(t =>
{
WriteLine($"The task completed with the {task.Status} status");
},
TaskContinuationOptions.OnlyOnFaulted);
Read();
Linguagem de código: C# ( cs )
Se você inserir valores mínimo e máximo válidos, a primeira continuação será executada e gravará o número aleatório no console:
Enter the min integer:10
Enter the max integer:100
Result: 81
Linguagem de código: texto simples ( texto simples )
Caso contrário, será executada a segunda continuação que grava uma mensagem de erro no console:
Enter the min integer:100
Enter the max integer:10
The task completed with the Faulted status
Linguagem de código: texto simples ( texto simples )
Resumo
- Use o
ContinueWith()
método daTask
classe para criar uma continuação que seja executada de forma assíncrona quando o processoTask
for concluído. - Utilize
TaskContinuationOptions
para criar continuações de acordo com o status da tarefa.