8 - Barreras en Windows

Las barreras sincronizan grupos de hilos al final de una etapa: todos deben llegar antes de que alguno continúe. Windows ofrece SYNCHRONIZATION_BARRIER; también se puede implementar una barrera manual con contador y variables de condición.

8.1 ¿Qué es una barrera de sincronización?

Es un punto de encuentro: varios hilos avanzan en paralelo y se detienen en la barrera hasta que todos lleguen. Luego, todos arrancan la siguiente etapa juntos, evitando que alguno se adelante.

8.2 SYNCHRONIZATION_BARRIER en Windows o implementación manual con contador + condition variable

Windows incluye SYNCHRONIZATION_BARRIER para facilitar esta coordinación. Cuando no está disponible (versiones antiguas) se puede crear una barrera manual con contador protegido por CRITICAL_SECTION y variables de condición para esperar y despertar.

8.3 InitializeSynchronizationBarrier() y EnterSynchronizationBarrier()

InitializeSynchronizationBarrier configura la barrera con el número de participantes. Cada hilo llama a EnterSynchronizationBarrier al finalizar una etapa; la función bloquea hasta que todos lleguen y luego libera a todos para la siguiente ronda.

8.4 Sincronización de etapas de un algoritmo paralelo en Windows

En algoritmos por fases (por ejemplo, simulaciones por iteraciones o pipelines), la barrera asegura que todos los hilos terminen la fase actual antes de empezar la siguiente, manteniendo consistencia de datos compartidos.

8.5 Casos de uso: iteraciones, simulaciones, fases de un pipeline

Común en métodos iterativos (Jacobi, Gauss-Seidel), juegos de celdas (Game of Life), render distribuido por tiles, y pipelines donde cada etapa depende de la salida completa de la anterior.

8.6 Aplicación en C (CLion, Windows): Cálculo paralelo por etapas con barrera

El programa crea varios hilos que procesan segmentos de un array en varias etapas. Al final de cada etapa todos llaman a EnterSynchronizationBarrier para esperar al resto. El hilo principal verifica el resultado final tras completar todas las fases.

#include <windows.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>

#define HILOS 4
#define N 16
#define ETAPAS 3

static int datos[N];
static SYNCHRONIZATION_BARRIER barrera;

unsigned __stdcall trabajador(void *arg) {
  int id = (int)(intptr_t)arg;
  int inicio = (N / HILOS) * id;
  int fin = inicio + (N / HILOS);

  for (int etapa = 1; etapa <= ETAPAS; etapa++) {
    for (int i = inicio; i < fin; i++) {
      datos[i] += etapa; /* trabajo simulado */
    }
    printf("[hilo %d] etapa %d completada\n", id, etapa);
    EnterSynchronizationBarrier(&barrera, SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY);
  }
  return 0;
}

int main(void) {
  for (int i = 0; i < N; i++) {
    datos[i] = i;
  }

  if (!InitializeSynchronizationBarrier(&barrera, HILOS, -1)) {
    fprintf(stderr, "No se pudo inicializar la barrera (%lu)\n", GetLastError());
    return EXIT_FAILURE;
  }

  HANDLE hilos[HILOS] = {0};
  for (int i = 0; i < HILOS; i++) {
    uintptr_t h = _beginthreadex(NULL, 0, trabajador, (void *)(intptr_t)i, 0, NULL);
    if (h == 0) {
      fprintf(stderr, "No se pudo crear el hilo %d\n", i);
      return EXIT_FAILURE;
    }
    hilos[i] = (HANDLE)h;
  }

  WaitForMultipleObjects(HILOS, hilos, TRUE, INFINITE);
  DeleteSynchronizationBarrier(&barrera);

  int ok = 1;
  for (int i = 0; i < N; i++) {
    int esperado = i + (ETAPAS * (ETAPAS + 1)) / 2; /* suma de 1..ETAPAS */
    if (datos[i] != esperado) {
      ok = 0;
      break;
    }
  }
  printf("Resultado final: %s\n", ok ? "correcto" : "incorrecto");

  for (int i = 0; i < HILOS; i++) {
    CloseHandle(hilos[i]);
  }
  return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}

Si tu versión de Windows no soporta SYNCHRONIZATION_BARRIER, reemplaza esa API por una barrera manual: un contador protegido con CRITICAL_SECTION y dos CONDITION_VARIABLE (para esperar y para reiniciar el ciclo de espera).

Diagrama de sincronización por barrera mostrando hilos esperando antes de continuar a la siguiente etapa