Resumo : neste tutorial, você aprenderá sobre o padrão assíncrono baseado em tarefas (TAP) em C# e como usar a classe C# Task para criar operações assíncronas.
Introdução ao padrão assíncrono baseado em tarefas (TAP)
C# introduziu um modelo de programação assíncrona (APM) que fornece uma maneira de executar operações vinculadas de E/S de forma assíncrona.
O APM é baseado em conceitos de retorno de chamada:
- Um método que representa uma operação assíncrona que aceita um retorno de chamada.
- Quando a operação assíncrona for concluída, o método invocará o retorno de chamada para notificar o código de chamada.
Como o APM é bastante difícil de entender, o C# introduziu o padrão assíncrono baseado em eventos (EAP) que executa operações assíncronas usando eventos.
Ao contrário do APM, no EAP, o método gera um evento quando a operação assíncrona é concluída, em vez de chamar um retorno de chamada.
O EAP é mais fácil de usar e tem melhor tratamento de erros do que o APM. No entanto, o EAP é bastante complexo de usar.
Para resolver esse problema, o C# introduziu a programação assíncrona baseada em tarefas (TAP), que simplifica bastante a programação assíncrona e facilita a gravação de código assíncrono.
O TAP consiste nos seguintes componentes principais:
- A
Task
classe – representa uma operação assíncrona. - As palavras-chave
async
/await
– definem métodos assíncronos e aguardam a conclusão das operações assíncronas. - API baseada em tarefas – um conjunto de classes que funcionam perfeitamente com a classe Task e palavras-chave async/await.
A TAP tem as seguintes vantagens:
- Desempenho aprimorado – O TAP pode melhorar o desempenho de um aplicativo, permitindo que ele execute operações vinculadas a E/S de forma assíncrona, liberando a CPU para outras tarefas.
- Código simplificado – TAP permite escrever código assíncrono como código síncrono, o que facilita a compreensão.
- Melhor gerenciamento de recursos – O TAP otimiza os recursos do sistema, permitindo que os aplicativos executem operações assíncronas sem bloquear threads.
Neste tutorial, focaremos na Task
classe e em como usá-la para executar operações assíncronas.
A classe Tarefa
A Task
aula é um conceito central da TAP. Representa uma operação assíncrona que pode ser executada de várias maneiras.
Suponha que você tenha um método que executa uma operação assíncrona chamada GetRandomNumber()
que retorna um número aleatório entre 1 e 100 assim:
static int GetRandomNumber()
{
Thread.Sleep(1000);
int randomNumber = (new Random()).Next(1, 100);
Console.WriteLine($"The random number is {randomNumber}");
return randomNumber;
}
Linguagem de código: C# ( cs )
Ao contrário de uma função regular, GetRandomNumber()
usa o
para atrasar um segundo antes de retornar um número aleatório. O objetivo Thread.Sleep
()Thread.Sleep()
é simular uma operação assíncrona que leva cerca de um segundo para ser concluída.
Executando uma tarefa
Para executar o
método de forma assíncrona, você cria um novo GetRandomNumber()
Task
objeto e chama o
método em uma expressão lambda passada ao GetRandomNumber()
Task
construtor do:
var task = new Task(() => GetRandomNumber());
Linguagem de código: C# ( cs )
e comece a executar a tarefa chamando o Start()
método do Task
objeto:
task.Start();
Linguagem de código: C# ( cs )
Junte tudo:
static int GetRandomNumber()
{
Thread.Sleep(1000);
int randomNumber = (new Random()).Next(1, 100);
Console.WriteLine($"The random number is {randomNumber}");
return randomNumber;
}
var task = new Task(() => GetRandomNumber());
task.Start();
Console.WriteLine("Start the program...");
Console.ReadLine();
Linguagem de código: C# ( cs )
Saída:
Start the program...
The random number is 65
Linguagem de código: C# ( cs )
Observe que o
método não bloqueia o thread principal, portanto você verá a seguinte mensagem primeiro:task.Start
()
Start the program...
Linguagem de código: C# ( cs )
….antes do número aleatório:
The random number is 65
Linguagem de código: C# ( cs )
O Console.ReadLine()
bloqueia o thread principal até que você pressione uma tecla. É usado para aguardar a Task
conclusão do thread filho agendado pelo objeto.
Se você não bloquear o thread principal, ele será encerrado após o programa exibir a mensagem “Inicie o programa…”.
Observe que o Task
construtor aceita muitas outras opções com as quais você não precisa se preocupar por enquanto.
Nos bastidores, o programa usa um pool de threads para executar a operação assíncrona. O Start()
método agenda a operação para execução.
Para provar isso, podemos exibir o ID do thread e se o thread pertence ou não ao pool de threads gerenciado:
static int GetRandomNumber()
{
var threadId = Thread.CurrentThread.ManagedThreadId;
var threadPool = Thread.CurrentThread.IsThreadPoolThread;
Console.WriteLine($"The thread #{threadId}, use a thread pool {threadPool}");
Thread.Sleep(1000);
int randomNumber = (new Random()).Next(1, 100);
Console.WriteLine($"The random number is {randomNumber}");
return randomNumber;
}
var task = new Task(() => GetRandomNumber());
task.Start();
Console.WriteLine("Start the program...");
Console.ReadLine();
Linguagem de código: JavaScript ( javascript )
Saída:
Start the program...
The thread #5, use a thread pool True
The random number is 97
Linguagem de código: PHP ( php )
A saída mostra que o ID do thread é 5 e o thread pertence a um conjunto de threads. Observe que você provavelmente verá um número diferente.
Como o código para criar um Task
objeto e iniciá-lo é bastante detalhado, você pode encurtá-lo usando o Run()
método estático da Task
classe:
Task.Run(() => GetRandomNumber());
Linguagem de código: C# ( cs )
O Run()
método enfileira a operação ( GetRandomNumber
) no conjunto de threads para execução.
Da mesma forma, você pode usar o StartNew()
método do Factory
objeto da Task
classe para criar uma nova tarefa e agendar sua execução:
Task.Factory.StartNew(() => GetRandomNumber());
Linguagem de código: C# ( cs )
Obtendo o resultado de uma tarefa
O Run()
método retorna um objeto Task< TResult
> que representa o resultado da operação assíncrona.
Em nosso exemplo, the GetRandomNumber()
retorna um número inteiro, portanto,
retorna o Task.Run
()Task<int>
objeto:
Task<int> task = Task.Run(() => GetRandomNumber());
Linguagem de código: C# ( cs )
Para obter o número retornado do GetRandomNumber()
método, você usa a Result
propriedade do objeto task:
task.Result
Linguagem de código: C# ( cs )
Junte tudo.
static int GetRandomNumber()
{
Thread.Sleep(1000);
int randomNumber = (new Random()).Next(1, 100);
return randomNumber;
}
// The main thread is blocked
// until the result is available
Task<int> task = Task.Run(() => GetRandomNumber());
Console.WriteLine($"The random number is {task.Result}");
Linguagem de código: C# ( cs )
Saída:
The random number is 15
Linguagem de código: C# ( cs )
Resumo
- Use programação assíncrona baseada em tarefas (TAP) para desenvolver programas assíncronos.
- Use a
Task
classe para executar operações assíncronas.