Processamento de grandes dados em C#: do LINQ aos fluxos paralelos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que C# é sua ferramenta secreta para Big Data?

\n\n

Quando se fala em processamento de grandes dados, logo vêm à mente Python com seu Pandas, Scala com Spark ou Java com Hadoop. Mas sejamos honestos: C# muitas vezes fica injustamente na sombra. E é uma pena. A plataforma .NET nos dá ferramentas tão poderosas como PLINQ, MemoryMappedFile, Parallel.For e fluxos assíncronos, que permitem processar gigabytes de dados em hardware comum sem complicações.

\n\n

Neste artigo, não vamos divagar em teoria — vamos direto ao ponto. Você aprenderá como extrair o máximo do seu processador ao processar grandes dados em C#, como evitar o OutOfMemoryException e como processar arquivos de dezenas de gigabytes como se fossem apenas um grande livro.

\n\n

O principal problema: como não matar a memória?

\n\n

Imagine: você tem um arquivo CSV de 50 GB com logs de servidor. Você o abre em um StreamReader comum e lê linha por linha. Tudo bem? Não. Se você tentar carregá-lo inteiro em um List<string> — olá, OutOfMemoryException. Mesmo que você tenha 64 GB de RAM, o .NET pode fragmentar a memória de tal forma que um objeto grande simplesmente não cabe no heap.

\n\n

Solução número um: streaming e chunking. Não carregue tudo de uma vez — processe em pedaços.

\n\n
public async IAsyncEnumerable<string[]> ReadCsvChunksAsync(string filePath, int chunkSize = 10000)\n{\n    using var reader = new StreamReader(filePath);\n    string? line;\n    var buffer = new List<string>(chunkSize);\n\n    while ((line = await reader.ReadLineAsync()) != null)\n    {\n        buffer.Add(line);\n        if (buffer.Count >= chunkSize)\n        {\n            yield return buffer.ToArray();\n            buffer.Clear();\n        }\n    }\n\n    if (buffer.Count > 0)\n        yield return buffer.ToArray();\n}\n
\n\n

Agora você pode processar gigabytes sem se preocupar com a memória:

\n\n
await foreach (var chunk in ReadCsvChunksAsync("logs.csv"))\n{\n    ProcessChunk(chunk); // sua lógica\n}\n
\n\n

Processamento paralelo: usando todos os núcleos da CPU

\n\n

Ok, estamos lendo dados em blocos. Mas se você tem um processador de 16 núcleos e os dados são processados sequencialmente — você está usando apenas 6% de sua capacidade. Hora de ativar a computação paralela em C#.

\n\n

PLINQ — Parallel LINQ

\n\n

A mágica mais simples é adicionar .AsParallel() à sua consulta LINQ. Mas com grandes dados é preciso ter cuidado: o PLINQ decide por conta própria quando paralelizar, o que pode levar a fragmentação excessiva.

\n\n
var numbers = Enumerable.Range(1, 10_000_000);\n\n// LINQ comum (lento)\nvar sum = numbers.Where(n => n % 2 == 0).Sum();\n\n// PLINQ (rápido, mas com cuidado)\nvar parallelSum = numbers\n    .AsParallel()\n    .WithDegreeOfParallelism(Environment.ProcessorCount)\n    .Where(n => n % 2 == 0)\n    .Sum();\n\nConsole.WriteLine($"Resultado: {parallelSum}");\n
\n\n

Mas PLINQ não é uma panaceia. Para processamento complexo de grandes dados em C#, é melhor usar Parallel.ForEach com controle manual de divisão.

\n\n

Parallel.ForEach com divisão em intervalos

\n\n
var allLines = File.ReadLines("huge_dataset.csv");\nvar processedCount = 0;\nvar lockObject = new object();\n\nParallel.ForEach(\n    allLines,\n    new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },\n    line =>\n    {\n        var result = ProcessLine(line); // sua função pesada\n        lock (lockObject)\n        {\n            processedCount++;\n            if (processedCount % 100_000 == 0)\n                Console.WriteLine($"Processados: {processedCount}");\n        }\n    });\n
\n\n

MemoryMappedFile: trabalhando com arquivos como se fossem memória RAM

\n\n

Quando o arquivo é grande demais para ser carregado na RAM, mas você pre

Blogs

Book Recommendations