C# - Concorrência , Paralelismo, Multithread e Assincronismo no .NET
Hoje veremos os conceitos de concorrência, paralelismo, multithreading e assincronismo aplicados na plataforma .NET. |
Os termos concorrência, paralelismo, multithreading e programação assíncrona muitas vezes são aplicados de forma intercambiáveis na plataforma .NET embora eles não sejam a mesma coisa.
Na verdade existe uma confusão na utilização dos termos e aos conceitos usados que estão relacionados com a execução assíncrona usando async e await.
Vamos apresentar cada conceito de forma objetiva e clara para tentar dirimir essa confusão.
Concorrência
Concorrência significa realizar mais de uma tarefa ao mesmo tempo.
Como exemplo temos as aplicações clientes que usam a concorrência para responder a uma entrada do usuário e gravar no banco de dados ou aplicações web que usam a concorrência para responder a um segundo request enquanto concluem o atendimento ao primeiro request.
A concorrência é necessária sempre que você precisar que uma aplicação realize uma tarefa enquanto esta trabalhando em outra. Desta forma realizar tarefas de forma concorrentes é o oposto de realizar tarefas de forma sequencial.
A concorrência não é a mesma coisa que multithreading.
Multithread
Multithread é uma forma de concorrência (mas não é a única) que usa mais de um thread, ou linhas de execução, para realizar mais de uma tarefa ao mesmo tempo.
Nota: Uma thread é uma sequência de instruções que pode ser executada independente de outro código.
Assim uma aplicação web inicia o processamento de um request em uma thread e a seguir, se outro request chegar enquanto ela esta processamento o primeiro request, ela inicia o processamento em outra thread.
Paralelismo
O paralelismo ou processamento em paralelo realiza muitas tarefas dividindo-as em várias threads(linhas de execução) que são executadas de forma concorrentes ou simultâneas.
Assim o
paralelismo usa a multithreading para maximizar o uso de vários
processadores. As CPUs modernas têm vários núcleos e, se houver muito trabalho a
fazer, então não faz sentido apenas um núcleo fazer todo o trabalho enquanto os
outros ficam ociosos. O processamento paralelo dividirá o trabalho entre várias
threads, que podem ser executadas independentemente em um núcleo diferente.
O paralelismo é um tipo de multithreading que é também um tipo de
concorrência.
A programação paralela deve ser usada sempre que você tiver uma boa quantidade de trabalho de computação que pode ser dividida em partes independentes de trabalho. Ela aumenta o uso da CPU temporariamente para melhorar o rendimento; isso é desejável em sistemas cliente onde as CPUs estão frequentemente ociosas, mas geralmente não é apropriado para sistemas de servidor.
A maioria dos servidores possui algum paralelismo integrado; por exemplo, a ASP.NET tratará várias solicitações em paralelo. Escrever código paralelo no servidor ainda pode ser útil em algumas situações (se você sabe que o número de usuários simultâneos sempre será baixo), mas em geral, a programação paralela no servidor funcionaria contra o paralelismo embutido e não forneceria qualquer benefício real.
Existem duas formas de paralelismo: paralelismo de dados e paralelismo de tarefas.
1- Paralelismo de dados é quando você tem um monte de itens de dados para processar e o processamento de cada parte dos dados é independente das outras partes.
2- Paralelismo de tarefas é quando você tem um conjunto de trabalho a fazer e cada parte do trabalho é independente das outras partes. O paralelismo de tarefas pode ser dinâmico; se uma peça de trabalho resultar em várias peças de trabalho adicionais, elas podem ser adicionadas ao conjunto de trabalho.
Outro tipo de concorrência é a programação assíncrona.
Assincronismo ou Programação assíncrona
A programação assíncrona permite usar as threads em nossos processos de uma maneira mais eficiente sendo uma forma de concorrência que usa recursos como future ou promisses e callbacks para evitar threads desnecessárias.
Um future ou promisse é um tipo que representa alguma operação que será concluída no futuro. Na plataforma .NET os tipos equivalentes usados são são Task e Task<TResult>. As APIs assíncronas mais antigas usam callbacks ou eventos em vez de future.
A programação
assíncrona é centrada na ideia de que alguma operação que é iniciada e será
concluída algum tempo depois. Enquanto a operação está em andamento, ela não
bloqueia a thread original; a thread que inicia a operação está livre para fazer
outro trabalho. Quando a operação for concluída, ele notifica seu
future
ou invoca seu callback de conclusão para permitir que o aplicativo saiba
que a operação foi concluída.
A programação assíncrona é uma forma poderosa de concorrência, mas, até
recentemente, exigia um código extremamente complexo. Na plataforma .NET temos o
C# com suporte a async e await que tornaram a programação assíncrona
quase tão fácil quanto a programação síncrona (não simultânea).
A programação assíncrona tem dois benefícios principais:
Os aplicativos .NET assíncronos modernos usam as palavras-chave: async e await.
A palavra-chave async é adicionada a uma declaração de método, e seu objetivo principal é habilitar a palavra-chave await dentro desse método, e, um método assíncrono deve retornar uma Task<T> se retornar um valor, ou retornar um Task se não retornar um valor. Esses tipos de tarefas representam futures; eles notificam o código de chamada quando o método assíncrono for concluído.
Na segunda parte do artigo vamos focar na programação assíncrona usando async e await.
"17 Quando
o vi (Jesus), caí aos seus pés como morto. Então ele colocou sua mão
direita sobre mim e disse: "Não tenha medo. Eu sou o primeiro e o último. 18
Sou aquele que vive. Estive morto mas agora estou vivo para todo o sempre! E
tenho as chaves da morte e do Hades."
Apocalipse 1:17,18
Referências:
VB .NET - Apresentando o conceito de Task
C# - Tasks x Threads. Qual a diferença
C# - Programação Paralela
.NET - Apresentando Parallel LINQ (PLINQ)