Concorrência em C#: threads, assincronia e goroutines na prática

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 em C#: como não deixar o programa entediado

\n\n

Imagine que você é um chef de cozinha. Você tem três bocas de fogão, um forno e uma batedeira. Se você fizer tudo sequencialmente — primeiro cortar a cebola, depois fritar, depois bater o creme — o almoço vai demorar uma eternidade. É muito mais eficiente ligar o forno enquanto corta os legumes e bater o creme enquanto a carne cozinha. Isso é concorrência — a capacidade de executar várias tarefas "simultaneamente", alternando entre elas para que os recursos não fiquem ociosos.

\n\n

Em C#, existem três ferramentas principais para isso: threads clássicas (Thread), tarefas modernas (Task) com async/await e o conceito de goroutines, implementado através de iteradores. Neste artigo, vamos analisar cada abordagem, comparar seu desempenho e mostrar quando usar cada uma. Se você programa em C# e quer que seu código voe, em vez de rastejar, está no lugar certo.

\n\n

Threads: artilharia pesada

\n\n

Uma thread é a maneira mais de baixo nível de organizar a concorrência. Cada thread é um "processo leve" separado dentro do seu programa, que tem sua própria pilha e contexto. Criar uma thread em C# é muito simples:

\n\n
using System;\nusing System.Threading;\n\nclass Program\n{\n    static void Main()\n    {\n        Thread thread = new Thread(DoWork);\n        thread.Start();\n        Console.WriteLine("Thread principal funcionando");\n        thread.Join(); // aguarda a conclusão\n    }\n\n    static void DoWork()\n    {\n        for (int i = 0; i < 5; i++)\n        {\n            Console.WriteLine($"Thread: {i}");\n            Thread.Sleep(200);\n        }\n    }\n}
\n\n

Aqui, criamos uma thread separada que conta até cinco, enquanto a thread principal exibe uma mensagem. Sem Join, o programa poderia terminar antes de chegar a cinco.

\n\n

Desvantagens das threads

\n
    \n
  • Peso elevado. Cada thread reserva cerca de 1 MB de memória para a pilha. Criar 1000 threads significa ocupar 1 GB à toa.
  • \n
  • Complexidade de sincronização. Para que duas threads não corrompam dados compartilhados, são necessários locks, mutexes e semáforos. Um erro e você tem um deadlock ou race condition.
  • \n
  • Troca de contexto. O sistema operacional gasta tempo alternando entre threads. Se houver mais threads do que núcleos de CPU, isso se torna um gargalo.
  • \n
\n\n

As threads são boas quando você precisa de paralelismo real em um processador multi-core para cálculos pesados. Mas para operações de E/S (trabalhar com arquivos, rede, banco de dados), elas são excessivas.

\n\n

Task e async/await: o padrão moderno

\n\n

A partir do .NET 4.5, a Microsoft propôs uma alternativa elegante — a Task Parallel Library (TPL) e as palavras-chave async/await. Não são threads puras, mas promessas: você diz ao compilador "inicie este trabalho e, quando terminar, retorne o resultado". Enquanto o trabalho é executado, a thread não é bloqueada, mas retorna ao pool.

\n\n
using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nclass Program\n{\n    static async Task Main()\n    {\n        Console.WriteLine("Iniciando download...");\n        string data = await DownloadDataAsync();\n        Console.WriteLine($"Baixado: {data.Length} caracteres");\n    }\n\n    static async Task<string> DownloadDataAsync()\n    {\n        using HttpClient client = new HttpClient();\n        string result = await client.GetStringAsync("https://example.com");\n        return result;\n    }\n}
\n\n

Observe: await não bloqueia a thread. Enquanto os dados são baixados pela rede, a thread retorna ao pool e pode atender outras requisições. Isso é ideal para servidores web e aplicações GUI — a interface não congela.

\n\n

Como funciona por baixo dos panos?

\n

Quando o compilador vê async, ele transforma o método em uma máquina de estados. Em cada await, o método "lembra" seu estado

Blogs

Book Recommendations