Parallel Programming
Parallel.ForEach, PLINQ, particionamento e cancelamento para workloads CPU-bound
Paralelismo de dados distribui itens independentes entre threads. Parallel.ForEach e um loop imperativo; PLINQ estende LINQ com execucao paralela. Ambos sao otimizados para CPU-bound β para I/O-bound, use async/await.
Quando paralelizar?
Paralelismo so vale quando o trabalho e CPU-bound, os itens sao independentes e a colecao e grande o suficiente para compensar o overhead de coordenacao.
CPU-bound vs I/O-bound
| Aspecto | CPU-bound | I/O-bound |
|---|---|---|
| Exemplo | Hash, compressao, calculo matematico | HTTP request, leitura de arquivo, query no banco |
| Gargalo | Processador | Latencia de rede/disco |
| Solucao | Parallel.ForEach / PLINQ | async/await + SemaphoreSlim |
| Threads | Usa cores reais da CPU | Threads ficam bloqueadas esperando β desperdicio |
| Escalabilidade | Limitada ao numero de cores | Limitada pela latencia do recurso externo |
Parallel.ForEach vs PLINQ β quando usar cada um
| Criterio | Parallel.ForEach | PLINQ (AsParallel) |
|---|---|---|
| Estilo | Imperativo β loop com body | Declarativo β pipeline LINQ |
| Quando preferir | Ja tem um loop body; quer paralelismo com minima cerimonia | Ja usa LINQ; quer operadores paralelos (filter/map/group) |
| Ordering | Nao garantido | AsOrdered() disponivel |
| Composabilidade | Baixa β um loop | Alta β encadeia operadores |
| Thread-local state | localInit/localFinally | Nao nativo |
| Particionamento | Partitioner.Create() explicito | Automatico pelo runtime |
| Resultado | Void β use ConcurrentBag para coletar | Retorna IEnumerable<T> |
Regra pratica: Se voce ja esta em LINQ, use PLINQ. Se voce tem um loop
foreach que quer paralelizar, use Parallel.ForEach. Ambos sao para CPU-bound β para I/O, use Task.WhenAll com SemaphoreSlim. Quando NAO paralelizar?
1) Quando o trabalho e I/O-bound β threads ficam bloqueadas esperando, nao processando. Use async/await. 2) Quando a colecao e pequena β o overhead de criar partitions e coordenar threads e maior que o ganho. 3) Quando os itens tem dependencias entre si β paralelismo assume independencia. 4) Quando voce esta dentro de um request handler ASP.NET β o ThreadPool ja esta compartilhado; paralismo compete com outros requests.