Programação paralela no .NET Framework 4 – Parte I
Introdução
O avanço de tecnologia nos últimos anos forneceu, a baixo custo, acesso a workstations com inúmeros CPUs. Facilmente encontramos hoje máquinas clientes com 2, 4 e até 8 núcleos, sem considerar os “super-servidores” com até 36 processadores :)
Da wikipedia:
A Unidade central de processamento (CPU, de acordo com as iniciais em inglês) ou o processador é a parte de um sistema de computador que executa as instruções de um programa de computador, e é o elemento primordial na execução das funções de um computador. Este termo tem sido usado na indústria de computadores pelo menos desde o início dos anos 1960[1]. A forma, desenho e implementação de CPUs têm mudado dramaticamente desde os primeiros exemplos, mas o seu funcionamento fundamental permanece o mesmo.
Fazendo uma analogia, seria muito interessante delegarmos tarefas no mundo real que podem ser executadas independentemente a pessoas diferentes, atingindo desta forma uma maior performance / produtividade na sua execução.
A computação paralela se baseia na idéia que um problema maior pode ser dividido em problemas menores, sendo resolvidos de forma paralela. Este pensamento é utilizado há algum tempo por HPC (High-performance computing), e através das facilidades dos últimos anos, assim como a preocupação com consumo de energia, tornaram esta idéia mais atrativa e de fácil acesso a qualquer ambiente.
No .NET Framework
A plataforma .NET apresenta um runtime, bibliotecas e ferramentas para fornecer uma base de acesso fácil e rápido à programação paralela, sem trabalhar diretamente com threads e thread pool.
Esta série de posts irá apresentar todos os recursos disponíveis, iniciando os estudos pela TPL, ou Task Parallel Library.
Task Parallel Library
A TPL é um conjunto de tipos localizados no namespace System.Threading e System.Threading.Tasks, a partir da versão 4 do framework.
A partir da versão 4 do framework, o TPL é a maneira recomendada para escrever código paralelo e multithreaded.
http://msdn.microsoft.com/en-us/library/dd460717(v=VS.100).aspx
Task Parallelism
O termo “task parallelism”, ou em uma tradução live paralelismo de tarefas, se refere a uma ou mais tarefas sendo executadas de forma simultanea.
Considere uma tarefa como um método. A maneira mais fácil de executar tarefas de forma paralela é o código abaixo:
Parallel.Invoke(() => TrabalhoInicial(), () => TrabalhoSeguinte());
O que acontece de verdade?
Por trás nos panos, esta instrução instancia de forma implícita objetos do tipo Task, responsável por representar uma operação assíncrona, não exatamente paralela:
public class Task : IAsyncResult, IDisposable
É possível instanciar Tasks de forma explícita, sendo uma alternativa mais complexa ao Parallel.Invoke.
var task = new Task(() => TrabalhoInicial());
task.Start();
Outra opção de instanciar uma Task e já executar sua tarefa é:
var t = Task<int>.Factory.StartNew(() => TrabalhoInicialComValor());
var t2 = Task<int>.Factory.StartNew(() => TrabalhoSeguinteComValor());
A diferença básica entre as duas abordagens é que a primeira tem início conhecido, mais utilizado quando não queremos que a instanciação e o agendamento da execução ocorra em uma só operação, como na segunda abordagem.
Data Parallelism
Ainda parte da TPL, o Data Parallelism se refere a cenários onde a mesma operação deva ser executada paralelamente em elementos de uma coleção ou array, através de instruções paralelas For e ForEach. A idéia básica é pegar cada elemento da coleção (ou array) e trabalhar com diversas threads concomitantemente.
A classe-chave para este cenário é a System.Threading.Tasks.Parallel
// Sequential version foreach (var item in sourceCollection) { Process(item); } // Parallel equivalent Parallel.ForEach(sourceCollection, item => Process(item));
Complicado né? :)
Demonstração
Acesse aqui um vídeo com exemplos (screencast).
Cuidado!
Apesar da imensa vontade de sair codificando, tome cuidado com alguns problemas básicos de paralelismo. Neste link é possível conhecer algumas situações.
Abraços.