Concorrência e Multithreading em C#: Nível Avançado
No desenvolvimento moderno em C#, concorrência e multithreading não são apenas extensões de capacidades, mas ferramentas fundamentais para criar aplicações rápidas, responsivas e escaláveis. No nível avançado, vamos além do simples uso de Task.Run e nos deparamos com as nuances dos agendadores, sincronização de contextos e padrões para trabalhar com dados compartilhados. Neste artigo, abordaremos os principais conceitos avançados de concorrência em C# (.NET) que o ajudarão a escrever código eficiente e seguro.
1. Task e ValueTask: Uso Aprofundado
A classe Task é a base da programação assíncrona em C#. No entanto, no nível avançado, é importante entender a diferença entre Task e ValueTask, bem como gerenciar o ciclo de vida das tarefas.
Quando usar ValueTask?
ValueTask é uma estrutura que evita a alocação de memória para um objeto Task quando o resultado já está disponível de forma síncrona. Isso é crítico para cenários de alto desempenho, como em métodos de cache.
public class CacheService{ private readonly Dictionary<string, string> _cache = new();
public async ValueTask<string> GetDataAsync(string key) { if (_cache.TryGetValue(key, out string value)) { // Resultado já existe, não cria Task return value; }
// Obtenção assíncrona de dados string data = await FetchFromDatabaseAsync(key); _cache[key] = data; return data; }
private async Task<string> FetchFromDatabaseAsync(string key) { await Task.Delay(100); // Simulação de atraso return $"Data for {key}"; }}Tratamento Avançado de Erros e Cancelamento
Use CancellationToken para cancelar corretamente operações longas. Combine vários tokens através de CancellationTokenSource.CreateLinkedTokenSource.
public async Task ProcessWithTimeoutAsync(CancellationToken externalToken){ using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken, timeoutCts.Token);
try { await LongRunningOperationAsync(linkedCts.Token); } catch (OperationCanceledException) { Console.WriteLine("Operação cancelada ou tempo limite excedido."); }}2. SynchronizationContext e Agendadores de Tarefas
Compreender SynchronizationContext é necessário para trabalhar com threads de UI (WPF, WinForms) e ASP.NET Core. No nível avançado, você pode criar seus próprios contextos de sincronização.
Configuração de Contexto para Pool de Threads
Por padrão, await captura o SynchronizationContext atual. Para evitar isso (e prevenir deadlocks), use ConfigureAwait(false).
public async Task ProcessDataAsync(){ // Este código é executado no contexto original (ex.: UI) await Task.Run(() => HeavyComputation()).ConfigureAwait(false);
// Após ConfigureAwait(false) — no contexto do pool de threads // Sem acesso a elementos da UI! Console.WriteLine("Executado na thread do pool");}Criação de um Agendador Personalizado
Você pode implementar seu próprio TaskScheduler, por exemplo, para limitar o número de tarefas executadas simultaneamente (análogo ao SemaphoreSlim no nível do agendador).
public class LimitedConcurrencyTaskScheduler : TaskScheduler{ private readonly SemaphoreSlim _semaphore; private int _runningTasks;
public LimitedConcurrencyTaskScheduler(int maxConcurrency) { _semaphore = new SemaphoreSlim(maxConcurrency);