Veja nesse artigo o que seria paralelismo e concorrência através de exemplos práticos em .NET com C#
Na computação, à medida que aumentaram as demandas por eficiência e velocidade, surgiram diferentes abordagens para maximizar o uso dos recursos de hardware e lidar com múltiplas tarefas ao mesmo tempo.
Duas dessas abordagens são o Paralelismo e a Concorrência.
Neste post rápido demonstrarei de forma prática o que seria cada um deles.
Vamos la 🙂
Começando pelo Paralelismo, ele se refere à execução simultânea de múltiplas tarefas, geralmente em diferentes núcleos de um processador. Isso permite que tarefas sejam executadas ao mesmo tempo, aproveitando ao máximo o poder de processamento disponível.
Para ficar mais claro, imagine o seguinte cenário: você está trabalhando em um projeto que recebe uma lista de e-mails e deseja enviá-los para uma fila (um broker qualquer).
string[] emails = ["email1@fiap.com.br", "email2@fiap.com.br", "email3@fiap.com.br"];
foreach (var email in emails)
{
SendEmailToQueue(email);
}
Console.WriteLine("Todos os e-mails foram enviados para a fila!");
Console.ReadLine();
static void SendEmailToQueue(string email)
{
Console.WriteLine($"Enviando {email} para a fila... {Environment.CurrentManagedThreadId}");
Thread.Sleep(500);
Console.WriteLine($"{email} foi enviado para a fila!");
}
Resultado:
Note que este serviço foi executado em 1551ms e rodou somente na main thread 1.
Para que possamos paralelizar este fluxo e fazer com que os e-mails cheguem mais rapido na fila, nos podemos utilizar a classe classe Parallel do namespace System.Threading.Tasks.
Somente um ponto de atênção aqui pessoal, esse tipo de processo não é exclusivo do C#, você pode trabalhar com paralelismo com outras linguagens de programação também.
Bom, vamos atualizar o nosso exemplo anterior para que possamos ver como trabalhar com paralelismo:
using System.Diagnostics;
var stopWatch = new Stopwatch();
stopWatch.Start();
string[] emails = ["email1@fiap.com.br", "email2@fiap.com.br", "email3@fiap.com.br"];
Parallel.ForEach(emails, SendEmailToQueue);
stopWatch.Stop();
Console.WriteLine($"Todos os e-mails foram enviados para a fila! {stopWatch.ElapsedMilliseconds}ms ");
Console.ReadLine();
void SendEmailToQueue(string email)
{
Console.WriteLine($"Enviando {email} para a fila... {Environment.CurrentManagedThreadId}");
Thread.Sleep(500);
Console.WriteLine($"{email} foi enviado para a fila!");
}
Neste exemplo nós atualizamos o foreach para o Parallel.foreach. (Bem conhecido pela comunidade de desenvolvedores .NET).
Resultado:
Note que tivemos um ganho significativo no processamento do nosso sistema, o que havia sido processado em 1551ms caiu para 543ms.
Isso foi possivel porque nós utilizamos oParallel.ForEach
, que divide o trabalho entre múltiplas threads, permitindo que as operações sejam executadas em paralelo.
Mas antes de avancarmos para o próximo tópico deste post, você sabe o que seria uma thread?
Caso a sua resposta seja não, uma thread é a menor unidade de processamento que um sistema operacional pode agendar.
Em termos simples, uma thread é uma sequência de instruções que pode ser executada independentemente.
Cada thread dentro de um processo compartilha o mesmo espaço de memória, o que permite que elas se comuniquem e compartilhem dados facilmente, mas também requer uma sincronização cuidadosa para evitar conflitos e inconsistências.
Agora avançando para o nosso próximo tópico sobre concorrência, ela se refere à capacidade de uma aplicação lidar com múltiplas tarefas, alternando entre elas para maximizar a eficiência.
Isso não significa que as tarefas estão sendo executadas ao mesmo tempo, mas sim que a aplicação gerencia várias tarefas de maneira que pareça simultânea.
Para que possamos entender como funciona concorrência na prática, imagine que você é um desenvolvedor trabalhando em um aplicativo de e-commerce. Você recebeu uma demanda para implementar uma funcionalidade que processa pedidos de clientes e, ao mesmo tempo, verifica se há atualizações de estoque para os produtos.
A seguir você tem um exemplo em C# que simula esse cenário usando concorrência:
using System.Diagnostics;
var stopWatch = new Stopwatch();
stopWatch.Start();
Task processOrders = ProcessOrdersAsync();
Task checkInventoryUpdates = CheckInventoryUpdatesAsync();
await Task.WhenAll(processOrders, checkInventoryUpdates);
stopWatch.Stop();
Console.WriteLine($"Processamento de pedidos e verificação de estoque concluídos. {stopWatch.ElapsedMilliseconds}ms");
async Task ProcessOrdersAsync()
{
Console.WriteLine($"Iniciando processamento de pedidos... {Environment.CurrentManagedThreadId}");
await Task.Delay(5000);
Console.WriteLine($"Processamento de pedidos concluído. {Environment.CurrentManagedThreadId}");
}
async Task CheckInventoryUpdatesAsync()
{
Console.WriteLine($"Verificando atualizações de estoque... {Environment.CurrentManagedThreadId}");
await Task.Delay(3000);
Console.WriteLine($"Atualizações de estoque verificadas. {Environment.CurrentManagedThreadId}");
}
Neste exemplo, ProcessOrdersAsync
simula o processamento de pedidos dos clientes, enquanto CheckInventoryUpdatesAsync
simula a verificação de atualizações no estoque.
Ambas as tarefas são iniciadas quase simultaneamente, mas são executadas de forma concorrente, o que significa que o sistema operacional e o runtime do .NET gerenciam a alternância entre as tarefas para maximizar a eficiência.
Resultado:
Notem que o sistema iniciou na thread principal e depois finalizou em outra thread. Este comportamento é devido ao uso do pool de threads pelo runtime do .NET para executar operações assíncronas.
Bom, espero que tenham entendido como Paralelismo e Concorrencia funcionam e que tenham gostado deste post.
Até mais pessoal.