Trabalhando com filas de tarefas em C#: guia prático

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

Introdução: por que usar filas de tarefas em C#?

\n

No desenvolvimento moderno em C# (.NET), frequentemente surge a necessidade de organizar o processamento assíncrono de dados. Um exemplo clássico é um servidor web que recebe requisições e precisa processá-las em segundo plano, sem bloquear a thread principal. Ou uma aplicação que baixa arquivos da internet e processa paralelamente os já baixados. Para esses cenários, o padrão «Producer-Consumer» (produtor-consumidor) é ideal, e sua base é uma fila de tarefas.

\n

A fila de tarefas permite transferir trabalho com segurança de uma thread (ou operação assíncrona) para outra, sincronizando o acesso a dados compartilhados. Neste artigo, veremos três formas principais de implementar filas em C#: ConcurrentQueue, BlockingCollection e o moderno System.Threading.Channels. Cada uma é adequada para cenários diferentes, e as analisaremos com exemplos práticos.

\n\n

1. ConcurrentQueue — fila thread-safe básica

\n

ConcurrentQueue<T> é a fila thread-safe mais simples do namespace System.Collections.Concurrent. Ela usa algoritmos lock-free (comparação e troca, CAS) para garantir alta performance. No entanto, ela não bloqueia a thread quando a fila está vazia — você precisará implementar a espera manualmente.

\n\n

Exemplo: carregando dados de múltiplas fontes

\n

Suponha que temos uma tarefa: baixar 10 páginas web e processá-las. Uma thread (Producer) adiciona URLs à fila, e várias threads (Consumers) as retiram e baixam.

\n
using System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class ConcurrentQueueExample\n{\n    public static async Task RunAsync()\n    {\n        var queue = new ConcurrentQueue<string>();\n        var urls = new[] { "https://example.com", "https://dotnet.microsoft.com" };\n\n        // Producer: adicionando tarefas\n        foreach (var url in urls)\n        {\n            queue.Enqueue(url);\n        }\n\n        // Consumers: processando tarefas\n        var tasks = new Task[3];\n        for (int i = 0; i < tasks.Length; i++)\n        {\n            tasks[i] = Task.Run(() =>\n            {\n                while (queue.TryDequeue(out var url))\n                {\n                    Console.WriteLine($"Processando: {url}");\n                    // Simulação de trabalho\n                    Task.Delay(500).Wait();\n                }\n            });\n        }\n\n        await Task.WhenAll(tasks);\n        Console.WriteLine("Todas as tarefas concluídas.");\n    }\n}
\n

Vantagens: simplicidade, alta performance com grande número de operações.
\nDesvantagens: não possui mecanismo embutido de espera (necessário loop com verificação), difícil limitar o tamanho da fila.

\n\n

2. BlockingCollection — fila com bloqueio e limite

\n

BlockingCollection<T> é um wrapper sobre uma coleção thread-safe que adiciona a capacidade de bloquear a thread consumidora enquanto a fila estiver vazia, e a thread produtora enquanto a fila estiver cheia. Por padrão, usa ConcurrentQueue, mas é possível usar qualquer coleção que implemente IProducerConsumerCollection<T>.

\n\n

Exemplo: processamento de pedidos com buffer limitado

\n

Imagine um sistema de processamento de pedidos. O produtor gera pedidos, e três consumidores os processam. O buffer é limitado a 10 elementos para não consumir muita memória.

\n
using System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class BlockingCollectionExample\n{\n    public static async Task RunAsync()\n    {\n        using var orders = new BlockingCollection<string>(boundedCapacity: 10);\n\n        // Producer\n        var producer = Task.Run(() =>\n        {\n            for (int i = 1; i <= 20; i++)\n                

Blogs

Book Recommendations