A programação assíncrona e concorrente são conceitos fundamentais para a criação de aplicações que precisam lidar com múltiplas operações ao mesmo tempo, como quando é necessário fazer requisições de rede, realizar processamento paralelo ou executar tarefas pesadas sem bloquear a interface do usuário. Em C#, o conceito de Task é uma peça central para implementar essas funcionalidades de maneira eficiente.
Uma Task é como uma tarefa que pode ser executada de forma independente, sem bloquear o restante do programa. Ela é usada para fazer tarefas em segundo plano enquanto o programa continua executando. Você pode esperar o resultado de uma Task usando o comando “await” ou controlar o que acontece depois dela com métodos como “ContinueWith” ou “WaitAll”.
Usando Task, conseguimos evitar que a “thread principal” (a parte do programa que controla a interface com o usuário) fique bloqueada. Isso permite que o programa faça várias ações ao mesmo tempo de maneira mais eficiente, sem travar ou demorar para responder.
O método “Task.Run” é utilizado para iniciar uma tarefa em uma thread separada. Isso é particularmente útil quando você precisa evitar o bloqueio da thread principal da aplicação, especialmente em aplicações com interface gráfica (GUI), onde o bloqueio da thread principal pode levar a uma interface congelada.
Console.WriteLine("Iniciando tarefa...");
Task.Run(() =>
{
// Simulando uma operação pesada
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Tarefa concluída.");
});
Console.WriteLine("Aguardando a tarefa...");
Console.ReadLine();
Em alguns casos, é necessário inserir um atraso na execução de uma tarefa sem bloquear a thread principal. Isso é facilmente feito com o método “Task.Delay”, que permite aguardar de forma assíncrona por um tempo especificado.
Console.WriteLine("Iniciando atraso...");
await Task.Delay(3000);
Console.WriteLine("Atraso concluído.");
Neste exemplo, a tarefa aguarda por 3 segundos sem bloquear a thread principal. O uso de “await” permite que o código continue executando enquanto aguarda o atraso.
Quando você precisa que uma tarefa assíncrona retorne um valor, você pode usar Task<T>. Essa classe permite que você execute uma operação assíncrona que retorna um valor do tipo T, que pode ser qualquer tipo de dado como “int”, “string”, ou até mesmo objetos complexos.
public static async Task Main()
{
Console.WriteLine("Iniciando tarefa de busca ...");
string resultado = await BuscarUsuario("José");
Console.WriteLine($"Usuário: {resultado} encontrado.");
}
public static async Task BuscarUsuario(string nome)
{
Console.WriteLine($"Buscando {nome} no banco...");
await Task.Delay(3000);
return "José Santos";
}
Neste exemplo, o método “BuscarUsuario” é uma tarefa assíncrona que retorna um valor do tipo “string”. A operação de busca é realizada após um atraso simulado de 3 segundos, e o valor retornado é o resultado da busca no “banco“.
Quando você tem várias tarefas em execução simultaneamente e precisa aguardar que todas terminem antes de prosseguir, o método “Task.WaitAll” é ideal. Ele permite aguardar a conclusão de várias tarefas de uma vez.
public static async Task Main()
{
Console.WriteLine("Iniciando múltiplas tarefas...");
Task task1 = Task.Run(() => ProcessaTarefa(1));
Task task2 = Task.Run(() => ProcessaTarefa(2));
Task task3 = Task.Run(() => ProcessaTarefa(3));
await Task.WhenAll(task1, task2, task3);
Console.WriteLine("Todas as tarefas concluídas.");
}
public static void ProcessaTarefa(int id)
{
System.Threading.Thread.Sleep(2000);
Console.WriteLine($"Tarefa {id} concluída.");
}
Neste exemplo, três tarefas são executadas simultaneamente. Usando “Task.WhenAll”, a execução do código só prossegue após todas as tarefas terem sido completadas.
O método “Task.ContinueWith” permite encadear a execução de uma tarefa após a conclusão de outra. Isso é útil quando você precisa realizar uma série de operações assíncronas em sequência.
Console.WriteLine("Iniciando tarefa...");
Task.Run(() =>
{
// Simulando tarefa inicial
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Tarefa 1 concluída.");
})
.ContinueWith(task =>
{
// Tarefa a ser executada após a primeira
System.Threading.Thread.Sleep(1000);
Console.WriteLine("Tarefa 2 concluída.");
});
Console.WriteLine("Aguardando tarefas...");
Console.ReadLine();
Neste exemplo, a segunda tarefa será executada assim que a primeira for concluída, sem a necessidade de aguardar explicitamente a primeira tarefa usando “await”.
Embora “Task” seja uma abstração importante para programação assíncrona, o “async/await” é uma maneira mais moderna e legível de lidar com operações assíncronas. A principal diferença entre os dois é que “async/await” proporciona uma sintaxe mais simples e menos propensa a erros ao escrever código assíncrono.
Quando usar Task:
Quando usar async/await:
Em muitas situações, você pode combinar as duas abordagens. Por exemplo, você pode usar “Task.Run” para executar algo em segundo plano e, dentro de um método “async”, aguardar essa tarefa com “await”.
Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aqui
Se é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aqui
O uso de Task no C# permite que desenvolvedores criem aplicativos mais rápidos e responsivos ao evitar o bloqueio de threads e executar operações em segundo plano. Técnicas como “Task.Run”, “Task.Delay”, “Task.WaitAll” e “Task.ContinueWith” são ferramentas muito úteis para gerenciar tarefas assíncronas e concorrentes. Quando combinado com “async/await”, você pode escrever código assíncrono de forma clara e eficiente, garantindo uma melhor experiência para o usuário.