Saltar al contenido
Explorar conocimiento

concepto · fundamentos · 6 min de lectura

Concurrencia

La concurrencia permite que varias tareas avancen durante el mismo periodo de tiempo, lo que exige coordinar estado compartido para mantener resultados predecibles.

No requiere conocimientos previos.

Alcance en breve

Cubre

  • concurrency as overlapping execution of multiple tasks
  • shared state, race conditions, coordination, and task independence at a conceptual level
  • the difference between concurrency and parallel execution

No cubre

  • distributed consensus and database isolation levels
  • low-level memory models and lock-free algorithms
  • language-specific scheduler internals

Supone

  • The reader understands functions, variables, and basic request/response or task execution terminology.

Resumen

Concurrencia es la capacidad de un sistema para manejar varias tareas que se solapan en el tiempo. No significa necesariamente que todas ejecuten al mismo instante físico; significa que sus pasos pueden intercalarse y afectar el orden en que se observan los resultados.

Importa porque casi todo sistema real atiende trabajo superpuesto: requests web, jobs en background, operaciones de red, timers, usuarios editando el mismo recurso o procesos compartiendo datos. Cuando esas tareas leen y escriben el mismo estado sin coordinación, aparecen resultados que dependen del timing.

Alcance y supuestos

Este paquete cubre concurrencia a nivel conceptual: tareas solapadas, estado compartido, condiciones de carrera, coordinación y diferencia básica entre concurrencia y paralelismo. Usa TypeScript asíncrono para el ejemplo porque muestra intercalación sin entrar en hilos reales.

No cubre consenso distribuido, niveles de aislamiento de bases de datos, memoria compartida de bajo nivel, algoritmos lock-free, actores, schedulers internos ni modelos de memoria de lenguajes específicos. Asume que entiendes funciones, variables y la idea de ejecutar tareas o requests.

Modelo mental

Piensa en dos cocineros usando la misma receta en una cocina pequeña. Pueden avanzar durante el mismo periodo: uno corta verduras mientras otro calienta la sartén. Eso es concurrencia. Si además ambos tienen espacio y herramientas para trabajar exactamente al mismo tiempo, hay paralelismo.

El problema aparece cuando ambos usan el mismo ingrediente sin coordinarse. Si los dos miran la caja y ven "queda un huevo", cada uno puede planear usarlo. Cuando llega el momento de cocinar, uno de los dos se queda sin huevo o ambos creen haberlo reservado. El error no viene de no saber cocinar; viene de leer una realidad compartida y actuar más tarde como si no hubiera cambiado.

En software pasa igual. Una tarea lee estado, se pausa esperando red o disco, y otra tarea modifica ese estado antes de que la primera termine. La concurrencia obliga a decidir qué partes pueden avanzar independientes y qué partes necesitan una frontera de coordinación.

Uso práctico

Usa concurrencia para mejorar capacidad de respuesta o throughput cuando las tareas pueden solaparse, pero coordina explícitamente el estado compartido:

  • ✅ Descargar tres recursos independientes a la vez puede reducir espera total.
  • ✅ Procesar requests concurrentes es normal si cada request tiene datos propios o usa transacciones/locks donde comparte estado.
  • ❌ Incrementar un contador compartido desde varias tareas sin coordinación puede perder actualizaciones.
  • ❌ Confundir concurrencia con paralelismo lleva a prometer velocidad cuando solo hay intercalación o espera no bloqueante.

Ejemplo trabajado: reservar el último cupo sin coordinar tareas

Supón que queda un solo cupo para una actividad y llegan dos reservas casi al mismo tiempo. Cada reserva lee el cupo disponible, espera una operación externa y luego confirma usando la lectura antigua.

const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

let availableSeats = 1;

async function reserveSeat(customer: string) {
  const seenSeats = availableSeats;

  await wait(10);

  if (seenSeats <= 0) {
    return `${customer}: sin cupo`;
  }

  availableSeats = seenSeats - 1;
  return `${customer}: reservado`;
}

Las dos tareas se solapan. Ambas pueden leer availableSeats cuando todavía vale 1, luego ambas continúan como si hubieran visto un cupo válido.

const results = await Promise.all([
  reserveSeat("Ana"),
  reserveSeat("Bruno"),
]);

console.log(results);
console.log({ availableSeats });

Una salida posible muestra el bug de concurrencia:

[ 'Ana: reservado', 'Bruno: reservado' ]
{ availableSeats: 0 }

El stock final no queda negativo, pero el sistema prometió dos reservas para un solo cupo. El error está en separar la lectura (seenSeats) de la escritura (availableSeats = seenSeats - 1) dejando un punto de intercalación entre ambas.

Una solución conceptual es proteger la sección crítica: la parte que revisa y modifica el cupo debe ejecutarse como una decisión coordinada. En una aplicación real esa frontera puede ser una transacción de base de datos, un lock, una cola, una restricción única o una operación atómica.

async function reserveSeatSafely(customer: string) {
  if (availableSeats <= 0) {
    return `${customer}: sin cupo`;
  }

  availableSeats -= 1;
  await wait(10);

  return `${customer}: reservado`;
}

Este ejemplo mueve la decisión crítica antes de la espera. No es una receta universal, pero muestra el principio: identifica qué estado compartido debe permanecer consistente y evita que otra tarea se intercale entre la verificación y el cambio.

Antes de introducir concurrencia, revisa cuatro preguntas:

  1. ¿Qué tareas pueden avanzar solapadas sin depender entre sí?
  2. ¿Qué datos se comparten entre tareas?
  3. ¿Hay una lectura seguida de una escritura que deba ser indivisible?
  4. ¿La coordinación correcta es transacción, lock, cola, operación atómica o evitar compartir estado?

Si las tareas son independientes, la concurrencia simplifica espera y mejora throughput. Si comparten estado, el diseño no termina hasta definir la frontera que hace predecible el resultado.

Evidencia

Conexiones

Relacionados y alternativas

Relacionado

Ver grafo local

Siguiente paso

Relacionado: Latencia

Fuentes citadas