Concorrência e Multithreading em C#: Um Guia Completo
Aplicações modernas exigem alta performance e capacidade de resposta. Concorrência e multithreading em C# são ferramentas essenciais que permitem executar múltiplas tarefas simultaneamente, usar eficientemente processadores multi-core e melhorar a experiência do usuário. Neste artigo, vamos detalhar os conceitos principais, começando por threads clássicas e terminando com async/await moderno e Parallel LINQ.
Fundamentos do Multithreading: A Classe Thread e o Thread Pool
Multithreading em C# começa com a classe System.Threading.Thread. Cada thread representa um caminho de execução separado dentro de um processo. No entanto, criar threads manualmente é uma operação cara. Para otimização, utiliza-se o thread pool (ThreadPool), que gerencia a reutilização de threads.
Exemplo de criação manual de uma thread:
using System;using System.Threading;
class Program{ static void Main() { Thread thread = new Thread(DoWork); thread.Start(); Console.WriteLine("A thread principal está trabalhando..."); thread.Join(); // Aguardar a conclusão }
static void DoWork() { Console.WriteLine("A thread está realizando trabalho."); }}Para uso mais simples, recomenda-se ThreadPool:
ThreadPool.QueueUserWorkItem(state => { Console.WriteLine("Tarefa no thread pool.");});Importante: A classe Thread tem uma API de baixo nível e não suporta retornar um resultado. Para aplicações modernas, utilize Task.
Task e TPL (Task Parallel Library)
A biblioteca TPL (System.Threading.Tasks) fornece uma abstração de alto nível — Task. Uma Task representa uma operação assíncrona que pode retornar um valor. Esta é a base para a concorrência moderna em C#.
Exemplo de uma Task sem valor de retorno:
using System;using System.Threading.Tasks;
class Program{ static async Task Main() { Task task = Task.Run(() => DoWork()); await task; Console.WriteLine("Concluído."); }
static void DoWork() { Console.WriteLine("Trabalho em Task."); }}Task com valor de retorno:
Task<int> task = Task.Run(() => { int result = 0; for (int i = 0; i < 1000000; i++) result += i; return result;});int sum = await task;Console.WriteLine($"Soma: {sum}");TPL também inclui Parallel.For e Parallel.ForEach para processamento paralelo de coleções:
Parallel.For(0, 10, i => { Console.WriteLine($"Iteração {i} na thread {Thread.CurrentThread.ManagedThreadId}");});Async/await: Programação Assíncrona
As palavras-chave async e await simplificam a escrita de código assíncrono, fazendo-o parecer com código síncrono. Elas não criam novas threads, mas alternam eficientemente o contexto de execução, liberando a thread para outras tarefas.
Exemplo de um método assíncrono:
using System;using System.Net.Http;using System.Threading.Tasks;
class Program{ static async Task Main() { string data = await FetchDataAsync("https://example.com"); Console.WriteLine(data); }
static async Task<string> FetchDataAsync(string url) { using HttpClient client = new HttpClient(); string result = await client.GetStringAsync(url); return result; }}Regras de uso:
- Um método com
asyncdeve retornarTask,Task<T>ouvoid(apenas para manipuladores de eventos