Concorrência e Multithreading em C#: Nível Avançado

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

Concorrência e Multithreading em C#: Nível Avançado

\n\n

No desenvolvimento moderno em C# (.NET), a capacidade de gerenciar eficientemente concorrência e multithreading diferencia um engenheiro experiente de um iniciante. O conhecimento básico sobre Thread e bloqueios simples já não é suficiente para criar aplicações de alto desempenho, responsivas e escaláveis. Neste artigo, abordaremos técnicas avançadas: desde uma compreensão profunda de async/await até o uso de System.Threading.Channels e TPL Dataflow. O material é voltado para desenvolvedores que já conhecem os fundamentos do multithreading.

\n\n

1. Mergulho Profundo em async/await e SynchronizationContext

\n\n

Muitos desenvolvedores usam async/await, mas não entendem completamente como o agendador de tarefas (Task Scheduler) e o SynchronizationContext gerenciam as continuações (continuations). Em um nível avançado, é importante controlar em qual thread o código será executado após o await.

\n\n

Por padrão, o await captura o SynchronizationContext atual (ou TaskScheduler). Em aplicações de interface do usuário (WPF, WinForms), isso retorna a execução para a thread principal. No ASP.NET Core e em aplicações de console, o contexto geralmente está ausente, e a continuação pode ser executada em qualquer thread do pool.

\n\n

Para ajuste fino, use ConfigureAwait(false). Isso informa ao runtime para não tentar retornar ao contexto original, melhorando o desempenho e prevenindo deadlocks em código de bibliotecas.

\n\n
public async Task<string> FetchDataAsync(HttpClient client)\n{\n    // A continuação não capturará o contexto\n    var result = await client.GetStringAsync("https://api.example.com")\n                              .ConfigureAwait(false);\n    // Este código será executado em qualquer thread do pool\n    return ProcessResult(result);\n}\n\npublic async Task<string> FetchDataWithContextAsync(HttpClient client)\n{\n    // A continuação retornará ao contexto original (ex.: thread da UI)\n    var result = await client.GetStringAsync("https://api.example.com");\n    // Este código será executado garantidamente no contexto original\n    return ProcessResult(result);\n}
\n\n

Dica Avançada: Crie seus próprios SynchronizationContext para cenários não padronizados, como organizar uma fila de tarefas com um número fixo de threads.

\n\n

2. Task Parallel Library (TPL) e Paralelismo de Dados

\n\n

A TPL fornece abstrações de alto nível para paralelismo. Parallel.For e Parallel.ForEach dividem automaticamente as coleções em partes (chunks) e as distribuem entre as threads. No entanto, em um nível avançado, é necessário entender como configurar o grau de paralelismo e o tratamento de exceções.

\n\n

Use ParallelOptions para limitar o número de threads, bem como ParallelLoopState para sair do loop antecipadamente.

\n\n
var options = new ParallelOptions\n{\n    MaxDegreeOfParallelism = Environment.ProcessorCount / 2 // Usamos metade dos núcleos\n};\n\nParallel.ForEach(sourceData, options, (item, state) =>\n{\n    try\n    {\n        // Operação pesada\n        ProcessItem(item);\n    }\n    catch (Exception ex)\n    {\n        // Registramos e interrompemos a execução\n        Console.WriteLine($"Erro: {ex.Message}");\n        state.Stop(); // Interrompe a execução do loop\n    }\n});
\n\n

Importante: Parallel.ForEach não garante a ordem de processamento. Se a ordem for importante, use Parallel.ForEach com índice ou PLINQ (AsParallel().AsOrdered()).

\n\n

3. Canais (System.Threading.Channels) para Producer-Consumer

\n\n

A classe Channel<T> surgiu no .NET Core 3.0 e se tornou uma ferramenta poderosa para cenários de produtor-consumidor. Ela é thread-safe, assíncrona e eficiente. Os canais são

Blogs

Book Recommendations