Introdução: por que usar filas de tarefas em C#?
Imagine que você está desenvolvendo um aplicativo que processa pedidos em uma loja online. Os clientes enviam requisições simultaneamente, e cada requisição leva tempo (por exemplo, verificação de pagamento ou envio de e-mail). Se processá-las sequencialmente, o aplicativo ficará lento e os usuários esperarão. A solução é usar uma fila de tarefas.
Uma fila de tarefas (Task Queue) é uma estrutura de dados que funciona pelo princípio FIFO (First In, First Out — "o primeiro a entrar é o primeiro a sair"). Em C# (.NET), as filas são implementadas em várias variações: desde a simples Queue<T> até a thread-safe ConcurrentQueue<T>. Neste artigo, veremos como criar filas, adicionar e remover tarefas, e como usá-las em programação assíncrona.
O artigo é voltado para iniciantes, então começaremos com o básico e gradualmente passaremos para exemplos mais complexos.
1. Fundamentos do Queue<T> em C#
A classe Queue<T> está no namespace System.Collections.Generic. É uma fila simples que não suporta multithreading — deve ser usada apenas em cenários single-thread.
Criação da fila e operações básicas
Vamos criar uma fila para armazenar tarefas de string:
using System;using System.Collections.Generic;
class Program{ static void Main() { // Criando uma fila de strings Queue<string> taskQueue = new Queue<string>();
// Adicionando tarefas (Enqueue) taskQueue.Enqueue("Enviar e-mail para o cliente"); taskQueue.Enqueue("Verificar status do pedido"); taskQueue.Enqueue("Atualizar banco de dados");
Console.WriteLine($"Quantidade de tarefas: {taskQueue.Count}");
// Removendo uma tarefa (Dequeue) — remove e retorna o primeiro elemento string firstTask = taskQueue.Dequeue(); Console.WriteLine($"Executando: {firstTask}");
// Visualizando a próxima tarefa sem removê-la (Peek) string nextTask = taskQueue.Peek(); Console.WriteLine($"Próxima: {nextTask}");
Console.WriteLine($"Tarefas restantes: {taskQueue.Count}"); }}Saída do programa:
Quantidade de tarefas: 3Executando: Enviar e-mail para o clientePróxima: Verificar status do pedidoTarefas restantes: 2Métodos importantes do Queue<T>
Enqueue(item)— adiciona um elemento ao final da fila.Dequeue()— remove e retorna o primeiro elemento. Se a fila estiver vazia, lança a exceçãoInvalidOperationException.Peek()— retorna o primeiro elemento sem removê-lo.Clear()— limpa a fila.Contains(item)— verifica se o elemento está na fila.
Importante: Queue<T> não é thread-safe. Se várias threads adicionarem ou removerem elementos simultaneamente, ocorrerão erros. Para cenários multithread, use ConcurrentQueue<T>.
2. Fila thread-safe: ConcurrentQueue<T>
Em aplicações reais, as tarefas são frequentemente adicionadas e processadas em threads diferentes. Para isso, o .NET oferece ConcurrentQueue<T> do namespace System.Collections.Concurrent.
Exemplo: Producer-Consumer (Produtor-Consumidor)
Imagine que uma thread (Producer) gera tarefas, e outra (Consumer) as executa.
using System;using System.Collections.Concurrent;using System.Threading.Tasks;
class Program{ static void Main() { ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
// Produtor: adiciona tarefas