Introducción: ¿por qué se necesitan colas de tareas en C#?
Imagine que está desarrollando una aplicación que procesa pedidos en una tienda en línea. Los clientes envían solicitudes simultáneamente y cada solicitud requiere tiempo (por ejemplo, verificar un pago o enviar un correo electrónico). Si se procesan secuencialmente, la aplicación se ralentizará y los usuarios esperarán. La solución es utilizar una cola de tareas.
Una cola de tareas (Task Queue) es una estructura de datos que funciona bajo el principio FIFO (First In, First Out — «el primero en entrar es el primero en salir»). En C# (.NET), las colas se implementan en varias variantes: desde la simple Queue<T> hasta la segura para subprocesos ConcurrentQueue<T>. En este artículo, analizaremos cómo crear colas, agregar y extraer tareas, y cómo usarlas en programación asíncrona.
El artículo está dirigido a principiantes, por lo que comenzaremos con lo básico y pasaremos gradualmente a ejemplos más complejos.
1. Conceptos básicos del trabajo con Queue<T> en C#
La clase Queue<T> se encuentra en el espacio de nombres System.Collections.Generic. Es una cola simple que no admite subprocesos múltiples; debe usarse solo en escenarios de un solo subproceso.
Creación de una cola y operaciones básicas
Vamos a crear una cola para almacenar tareas de cadena:
using System;using System.Collections.Generic;
class Program{ static void Main() { // Creamos una cola de cadenas Queue<string> taskQueue = new Queue<string>();
// Agregamos tareas (Enqueue) taskQueue.Enqueue("Enviar correo electrónico al cliente"); taskQueue.Enqueue("Verificar estado del pedido"); taskQueue.Enqueue("Actualizar base de datos");
Console.WriteLine($"Cantidad de tareas: {taskQueue.Count}");
// Extraemos una tarea (Dequeue) — elimina y devuelve el primer elemento string firstTask = taskQueue.Dequeue(); Console.WriteLine($"Ejecutando: {firstTask}");
// Vemos la siguiente tarea sin eliminarla (Peek) string nextTask = taskQueue.Peek(); Console.WriteLine($"Siguiente: {nextTask}");
Console.WriteLine($"Tareas restantes: {taskQueue.Count}"); }}Salida del programa:
Cantidad de tareas: 3Ejecutando: Enviar correo electrónico al clienteSiguiente: Verificar estado del pedidoTareas restantes: 2Métodos importantes de Queue<T>
Enqueue(item)— agrega un elemento al final de la cola.Dequeue()— elimina y devuelve el primer elemento. Si la cola está vacía, lanza una excepciónInvalidOperationException.Peek()— devuelve el primer elemento sin eliminarlo.Clear()— limpia la cola.Contains(item)— verifica si un elemento está en la cola.
Importante: Queue<T> no es segura para subprocesos. Si varios subprocesos agregan o extraen elementos simultáneamente, se producirán errores. Para escenarios de subprocesos múltiples, use ConcurrentQueue<T>.
2. Cola segura para subprocesos: ConcurrentQueue<T>
En aplicaciones reales, las tareas a menudo se agregan y procesan en diferentes subprocesos. Para esto, en .NET existe ConcurrentQueue<T> del espacio de nombres System.Collections.Concurrent.
Ejemplo: Productor-Consumidor (Producer-Consumer)
Imagine que un subproceso (Producer) genera tareas y otro (Consumer) las ejecuta.
using System;using System.Collections.Concurrent;using System.Threading.Tasks;
class Program{ static void Main() { ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
// Productor: agrega tareas