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.

Input CollectionPartitionerWorker Thread 1Worker Thread 2Worker Thread 3Worker Thread 4Merge ResultsOutput

CPU-bound vs I/O-bound

AspectoCPU-boundI/O-bound
ExemploHash, compressao, calculo matematicoHTTP request, leitura de arquivo, query no banco
GargaloProcessadorLatencia de rede/disco
SolucaoParallel.ForEach / PLINQasync/await + SemaphoreSlim
ThreadsUsa cores reais da CPUThreads ficam bloqueadas esperando β€” desperdicio
EscalabilidadeLimitada ao numero de coresLimitada pela latencia do recurso externo

Parallel.ForEach vs PLINQ β€” quando usar cada um

CriterioParallel.ForEachPLINQ (AsParallel)
EstiloImperativo β€” loop com bodyDeclarativo β€” pipeline LINQ
Quando preferirJa tem um loop body; quer paralelismo com minima cerimoniaJa usa LINQ; quer operadores paralelos (filter/map/group)
OrderingNao garantidoAsOrdered() disponivel
ComposabilidadeBaixa β€” um loopAlta β€” encadeia operadores
Thread-local statelocalInit/localFinallyNao nativo
ParticionamentoPartitioner.Create() explicitoAutomatico pelo runtime
ResultadoVoid β€” use ConcurrentBag para coletarRetorna 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.