Reunimos aplicaciones habituales de la concurrencia en Windows: pools de hilos, productor-consumidor, cálculos paralelos, simulación de eventos y procesamiento de archivos. Cerramos con un mini servidor de tareas implementado en C usando un thread pool.
Un thread pool mantiene un conjunto fijo de hilos listos para ejecutar trabajos sin pagar el costo de crearlos y destruirlos cada vez. Los trabajos se encolan y los hilos libres los toman.
Reduce la latencia de tareas cortas y mantiene estable el uso de recursos frente a oleadas de solicitudes.
Combinar semáforos para contar slots y condition variables para despertar hilos cuando cambian los estados permite buffers acotados eficientes, sin busy waiting.
En Windows, los semáforos pueden coordinar cupos y las condition variables reducen el tiempo bloqueado innecesario.
Dividir un arreglo grande en segmentos y asignar cada segmento a un hilo reduce el tiempo total. Se sincroniza al final con WaitForMultipleObjects o una barrera.
Para un balance parejo, calcula el rango de cada hilo en función del tamaño del arreglo y el número de hilos.
Cada hilo simula un sensor que emite eventos periódicos. Un hilo agregador los consume y los registra o procesa, coordinando con colas seguras.
Se pueden variar frecuencias y prioridades para imitar sensores críticos frente a sensores de diagnóstico.
Varios hilos leen archivos en paralelo para parsear y transformar datos. Se controla el número de hilos para no saturar I/O y se sincroniza el agregado de resultados.
Usa colas para distribuir rutas de archivo y un lock dedicado para consolidar estadísticas o escribir salidas.
El programa crea un pool de hilos fijo, mantiene una cola de tareas protegida con CRITICAL_SECTION y usa CONDITION_VARIABLE para notificar tareas nuevas. El usuario encola trabajos desde la consola; los hilos las ejecutan en paralelo.
Las tareas incluyen sumas simples y transformación de texto, pero puedes agregar procesamiento de archivos pequeños o simulaciones ligeras.
#include <windows.h>
#include <process.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_TAREAS 64
#define POOL_SIZE 4
typedef enum { TAREA_SUMA, TAREA_MAYUS, TAREA_QUIT } TipoTarea;
typedef struct {
TipoTarea tipo;
int a, b;
char texto[64];
} Tarea;
static Tarea cola[MAX_TAREAS];
static int head = 0;
static int tail = 0;
static int count = 0;
static CRITICAL_SECTION cs;
static CONDITION_VARIABLE cvNoVacia;
static CONDITION_VARIABLE cvNoLlena;
static volatile LONG detener = 0;
static int seguir(void) {
return InterlockedCompareExchange(&detener, 0, 0) == 0;
}
static void encolar(const Tarea *t) {
cola[tail] = *t;
tail = (tail + 1) % MAX_TAREAS;
count++;
}
static int desencolar(Tarea *t) {
if (count == 0) return 0;
*t = cola[head];
head = (head + 1) % MAX_TAREAS;
count--;
return 1;
}
static void procesar_tarea(const Tarea *t) {
switch (t->tipo) {
case TAREA_SUMA:
printf("[pool] suma %d + %d = %d\n", t->a, t->b, t->a + t->b);
break;
case TAREA_MAYUS: {
char buffer[64];
size_t i = 0;
for (; i < sizeof(buffer) - 1 && t->texto[i]; i++) {
char c = t->texto[i];
buffer[i] = (char)((c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c);
}
buffer[i] = '\\0';
printf("[pool] mayus: %s\n", buffer);
break;
}
case TAREA_QUIT:
/* manejada en el hilo principal */
break;
}
}
unsigned __stdcall trabajador(void *arg) {
int id = (int)(intptr_t)arg;
(void)id;
while (seguir()) {
EnterCriticalSection(&cs);
while (count == 0 && seguir()) {
SleepConditionVariableCS(&cvNoVacia, &cs, INFINITE);
}
if (!seguir()) {
LeaveCriticalSection(&cs);
break;
}
Tarea t;
(void)desencolar(&t);
WakeConditionVariable(&cvNoLlena);
LeaveCriticalSection(&cs);
procesar_tarea(&t);
}
return 0;
}
static void encolar_suma(int a, int b) {
Tarea t = { .tipo = TAREA_SUMA, .a = a, .b = b };
encolar(&t);
}
static void encolar_mayus(const char *texto) {
Tarea t = { .tipo = TAREA_MAYUS, .a = 0, .b = 0 };
snprintf(t.texto, sizeof(t.texto), "%s", texto);
encolar(&t);
}
int main(void) {
InitializeCriticalSection(&cs);
InitializeConditionVariable(&cvNoVacia);
InitializeConditionVariable(&cvNoLlena);
HANDLE hilos[POOL_SIZE] = {0};
for (int i = 0; i < POOL_SIZE; i++) {
uintptr_t h = _beginthreadex(NULL, 0, trabajador, (void *)(intptr_t)i, 0, NULL);
hilos[i] = (HANDLE)h;
}
puts("Mini servidor de tareas. Comandos:");
puts(" s a b -> suma a+b");
puts(" m texto -> convierte a MAYUS");
puts(" q -> salir");
char linea[128];
while (seguir() && fgets(linea, sizeof(linea), stdin)) {
if (linea[0] == 'q') {
break;
} else if (linea[0] == 's') {
int a, b;
if (sscanf(linea, "s %d %d", &a, &b) == 2) {
EnterCriticalSection(&cs);
while (count == MAX_TAREAS) {
SleepConditionVariableCS(&cvNoLlena, &cs, INFINITE);
}
encolar_suma(a, b);
WakeConditionVariable(&cvNoVacia);
LeaveCriticalSection(&cs);
} else {
puts("Formato: s a b");
}
} else if (linea[0] == 'm') {
char texto[64] = {0};
if (sscanf(linea, "m %63[^\n]", texto) == 1) {
EnterCriticalSection(&cs);
while (count == MAX_TAREAS) {
SleepConditionVariableCS(&cvNoLlena, &cs, INFINITE);
}
encolar_mayus(texto);
WakeConditionVariable(&cvNoVacia);
LeaveCriticalSection(&cs);
} else {
puts("Formato: m texto");
}
} else {
puts("Comando no reconocido.");
}
}
InterlockedExchange(&detener, 1);
EnterCriticalSection(&cs);
WakeAllConditionVariable(&cvNoVacia);
LeaveCriticalSection(&cs);
WaitForMultipleObjects(POOL_SIZE, hilos, TRUE, INFINITE);
for (int i = 0; i < POOL_SIZE; i++) {
CloseHandle(hilos[i]);
}
DeleteCriticalSection(&cs);
return 0;
}
Prueba con varias tareas seguidas para ver cómo el pool reparte el trabajo. Ajusta POOL_SIZE y MAX_TAREAS según la carga y el costo de cada tarea.
Si las tareas tardan mucho, agrega timeouts o cancelación para evitar que el pool quede ocupado indefinidamente.
CRITICAL_SECTION dentro del mismo proceso; reserva mutex para sincronización entre procesos.