Tema 6: Monitores
1. Introducción y motivación
En los temas anteriores estudiamos cómo resolver la exclusión mutua y la sincronización mediante semáforos. Aunque los semáforos son primitivas muy potentes a nivel de sistema operativo, presentan importantes inconvenientes cuando se construyen sistemas software complejos:
- Variables globales no estructuradas: Los semáforos se declaran como variables globales compartidas, lo que impide un diseño modular y desacoplado acorde a la ingeniería del software moderna.
- Ambigüedad funcional: Resulta difícil distinguir a simple vista si un semáforo se está empleando para exclusión mutua (semáforo binario o mutex) o para sincronización condicional (señalización entre hilos).
- Código de sincronización disperso: Las operaciones
wait()ysignal()están esparcidas por los métodos de distintos procesos o hilos. Si un hilo olvida ejecutar unsignal(), se desvía por unreturnprematuro o lanza una excepción no capturada, el recurso queda bloqueado indefinidamente (deadlock). - Ausencia de control en tiempo de compilación: En lenguajes de bajo nivel, el compilador no puede verificar si el acceso a los datos compartidos está correctamente protegido; cualquier hilo puede leer o modificar variables compartidas sin adquirir previamente el semáforo correspondiente.
Para resolver estas deficiencias, Per Brinch Hansen (1973) y C.A.R. Hoare (1974) introdujeron el concepto de Monitor como una construcción lingüística de alto nivel pensada para encapsular datos y sincronización dentro de una misma entidad abstracta.
2. Comparativa entre semáforos y monitores
| Criterio | Semáforos (Dijkstra, 1965) | Monitores (Hoare / Brinch Hansen, 1974) |
|---|---|---|
| Nivel de abstracción | Bajo nivel (primitiva del sistema operativo). | Alto nivel (construcción del lenguaje de programación). |
| Localización de datos | Variables globales accesibles desde cualquier hilo. | Variables privadas encapsuladas dentro del monitor. |
| Exclusión mutua | Explícita: El programador debe recordar invocar wait() y signal(). | Implícita: Garantizada automáticamente por el compilador y el entorno de ejecución. |
| Sincronización | Mediante el mismo contador entero del semáforo. | Mediante Variables de Condición independientes (delay/resume). |
| Propensión a errores | Alta: Un fallo puntual puede congelar todo el sistema. | Reducida: El acceso a los datos solo es posible a través de métodos públicos seguros. |
Semáforos: Sincronización dispersa y datos desprotegidos.
Monitores: Encapsulación de estado, cerrojo y métodos.
3. Modelo de monitor: concepto y funcionamiento
Un Monitor es un Tipo Abstracto de Datos (TAD) que encapsula:
- El estado privado (datos y variables compartidas).
- Un conjunto de operaciones o procedimientos públicos que constituyen la única interfaz de acceso a dicho estado.
- Un mecanismo automático de exclusión mutua: en cualquier instante de tiempo, como máximo un hilo puede estar ejecutando código dentro del monitor.
MONITOR
┌────────────────────────────────────────────────────────────────────────┐
│ Cola de Entrada al Monitor (Hilos esperando entrar) │
│ ──► [ Hilo A ] [ Hilo B ] [ Hilo C ] ──► (Cerrojo del Monitor) │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Procedimiento en ejecución (Máx. 1 hilo activo) │ │
│ │ │ │
│ │ Variables de estado privadas (ocultas): │ │
│ │ int saldo; int ocupados; Cola buffer; │ │
│ │ │ │
│ │ Variables de Condición (Hilos esperando condiciones lógicas): │ │
│ │ cond_t hay_sitio; ──► Cola FIFO: [ Hilo D ] [ Hilo E ] │ │
│ │ cond_t hay_elementos; ──► Cola FIFO: [ Hilo F ] │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Salida del monitor (libera el cerrojo para el siguiente hilo) │
└────────────────────────────────────────────────────────────────────────┘

Estructura formal de un monitor con colas de condición.
3.1. Sintaxis canónica en pseudocódigo
monitor NombreMonitor:
# 1. Variables privadas internas (estado del recurso)
variables locales;
condition c1, c2; # Variables de condición
# 2. Interfaz pública exportada
export operacion1, operacion2;
procedure operacion1(parámetros):
# Código ejecutado en exclusión mutua
...
procedure operacion2(parámetros):
# Código ejecutado en exclusión mutua
...
# 3. Código de inicialización (se ejecuta al instanciar el monitor)
inicializacion:
# Estado inicial
3.2. Reglas fundamentales de ejecución
- Monitor libre: Si un hilo invoca un procedimiento del monitor y ningún otro hilo está dentro, accede de inmediato y toma el cerrojo implícito.
- Monitor ocupado: Si otro hilo ya está ejecutando un procedimiento dentro del monitor, el hilo invocador se suspende y queda encolado en la cola de entrada al monitor.
- Salida del monitor: Cuando el hilo activo finaliza la ejecución de su procedimiento, el cerrojo se libera automáticamente y se permite el paso al primer hilo de la cola de entrada.
4. Variables de condición y sus operaciones
La exclusión mutua por sí sola no basta: con frecuencia un hilo entra al monitor pero comprueba que el estado actual no le permite continuar (por ejemplo, un consumidor entra pero el buffer está vacío). Si se quedara esperando dentro del monitor, bloquearía a todos los demás hilos (incluidos los productores que podrían llenar el buffer), provocando un interbloqueo.
Para solucionar esto, los monitores incorporan Variables de Condición. Una variable de condición no almacena ningún valor ni contador: es únicamente un nombre para una cola de espera de hilos.

Un hilo que no puede continuar se suspende en la variable de condición y cede el monitor.
Sobre una variable de condición c se definen las siguientes operaciones primitivas:
4.1. Operación delay(c) (o wait(c))
- El hilo invocador se suspende y se coloca al final de la cola de espera de la condición
c. - Libera atómicamente el monitor, permitiendo que otro hilo de la cola de entrada (o de cortesía) pueda acceder.
- El hilo permanecerá dormido hasta que otro proceso ejecute un
resume(c).
4.2. Operación resume(c) (o signal(c))
- Si hay hilos esperando en la cola de la condición
c, despierta exactamente al primero de ellos. - Si la cola de
cestá vacía, la operación no tiene ningún efecto (es una operación sin memoria, a diferencia de los semáforos donde el contador se incrementaría).
4.3. Operación empty(c)
- Función booleana de consulta: devuelve
truesi la cola de la condicióncno tiene ningún hilo esperando, yfalseen caso contrario.
En los semáforos, la operación signal() incrementa el contador de permisos; si ningún hilo estaba esperando, la señal queda almacenada para el futuro.
En las variables de condición de un monitor, la operación resume(c) carece de memoria. Si se invoca resume(c) cuando la cola de c está vacía (empty(c) == true), la señal se pierde definitivamente en el éter. No se incrementa ningún contador ni se recuerda para futuros hilos.
4.4. Especificación de prioridades en colas de condición
En la formulación formal de Hoare, la operación de espera admite un parámetro de rango o prioridad: delay(c, p).
Los hilos encolados en la variable de condición se ordenan de menor a mayor valor de (donde menor valor numérico representa mayor prioridad). Al ejecutar resume(c), se extrae y despierta preferentemente al hilo con menor , lo que permite modelar directamente planificadores de recursos con prioridad (como analizaremos en la implementación en C con POSIX).

Ciclo de suspensión con delay(c) y reactivación mediante resume(c).
5. Políticas de reanudación y señalización
Cuando un hilo señalizador (S) ejecuta resume(c) y despierta a un hilo durmiente (W), surge un dilema fundamental de diseño:
Dado que en un monitor solo puede haber un hilo activo a la vez, ¿cuál de los dos hilos debe continuar ejecutando dentro del monitor inmediatamente después del
resume(c)?
La respuesta a esta pregunta define la política de señalización del monitor. Existen tres disciplinas principales:
┌─────────────────────────────────────────────────────────────┐
│ POLÍTICAS DE SEÑALIZACIÓN EN MONITORES │
├──────────────────────────────┬──────────────────────────────┤
│ 1. Hoare (IRR / Urgent Wait) │ 2. Mesa / Java / POSIX (SC) │
├──────────────────────────────┼──────────────────────────────┤
│ - Señalar y Esperar Urgente │ - Señalar y Continuar │
│ - W se ejecuta de INMEDIATO │ - S continúa ejecutando │
│ - S cede monitor y va a cola │ - W pasa a competir con E │
│ de cortesía/urgente │ │
│ - Condición garantizada │ - Condición puede ser robada │
│ - Guarda con if es suficiente│ - Guarda con while obligada │
└──────────────────────────────┴──────────────────────────────┘
5.1. Señalar y esperar urgente (Signal-and-Urgent-Wait / Hoare / IRR)
Esta política, introducida por C.A.R. Hoare, también se conoce como IRR (Immediate Resumption Requirement):
- Mecanismo: Cuando el hilo señalizador S ejecuta
resume(c), cede el monitor de forma inmediata y pasa a una cola especial de alta prioridad llamada cola de cortesía o cola urgente. El hilo despertado W toma el control del monitor en ese mismo instante. - Prioridad en el acceso al monitor:
- Garantía del Invariante: Como el hilo despertado entra al monitor inmediatamente sin que ningún otro proceso pueda intercalarse, la condición por la que esperaba sigue siendo estrictamente cierta.
- Consecuencia sintáctica: Basta con usar una guarda condicional simple:
if (not condicion):delay(c)# Aquí la condición es 100% segura
5.2. Señalar y continuar (Signal-and-Continue / Mesa / Java / POSIX)
Diseñada por Lampson y Redell en Xerox PARC para el sistema operativo Mesa (1980), es la semántica adoptada por Java (notify), POSIX Threads (pthread_cond_signal) y Python (notify):
- Mecanismo: Cuando el señalizador S ejecuta
resume(c), no cede el monitor: continúa ejecutando su código hasta salir normalmente o bloquearse. El hilo despertado W pasa a la cola de listos para competir por el monitor junto con los hilos de la cola de entrada. - Prioridad en el acceso:
- El fenómeno del "Robo de Condición" (Condition Theft):
Entre el momento en que W es despertado y el momento en que finalmente consigue entrar al monitor, otro hilo recién llegado de la cola de entrada puede colarse y consumir el recurso liberado. - Consecuencia sintáctica OBLIGATORIA: Es imperativo evaluar la guarda en un bucle
while:while (!condicion) {wait();}// Al despertar, reevalúa la condición antes de continuar
5.3. Señalar y salir (Signal-and-Exit / Brinch Hansen)
- El hilo señalizador solo puede invocar
resume(c)como la última instrucción de su procedimiento, saliendo del monitor en ese mismo acto. - Elimina la necesidad de colas de cortesía y garantiza la preservación de la condición, pero restringe la expresividad del lenguaje.
5.4. Traza comparativa paso a paso: IRR (Hoare) frente a Mesa (Java)
Analicemos la ejecución de la siguiente secuencia de eventos con cuatro hilos ():
- entra al monitor y ejecuta
delay(c). - entra al monitor y ejecuta
delay(c). - entra al monitor, realiza un cálculo y ejecuta
resume(c). - Inmediatamente después, intenta entrar al monitor desde fuera.
Traza bajo política IRR (Hoare / Immediate Resumption)
Prioridad: Entrada () < Cola Urgente () < Despertado ()
| Paso | Acción realizada | Dentro del monitor | Cola urgente () | Cola de condición () | Cola de entrada () |
|---|---|---|---|---|---|
| 1 | entra al monitor | — | — | — | |
| 2 | ejecuta delay(c) (se bloquea y cede) | (vacío) | — | — | |
| 3 | entra al monitor | — | — | ||
| 4 | ejecuta delay(c) (se bloquea y cede) | (vacío) | — | — | |
| 5 | entra al monitor | — | — | ||
| 6 | intenta entrar | — | |||
| 7 | ejecuta resume(c) cede a | ||||
| 8 | termina y sale prioridad a la cola urgente | — | |||
| 9 | termina y sale entra la cola de entrada | — | — | ||
| 10 | termina y sale | (vacío) | — | — |
El orden en que los hilos ejecutan sus operaciones finales dentro del monitor es: ( sigue esperando en la condición ).
Traza bajo política Mesa / Java (Signal-and-Continue)
Prioridad: Despertado () = Entrada () < Señalizador ()
| Paso | Acción realizada | Dentro del monitor | Cola de condición () | Hilos listos compitiendo |
|---|---|---|---|---|
| 1-4 | y ejecutan delay(c) | (vacío) | — | |
| 5 | entra al monitor | — | ||
| 6 | intenta entrar | |||
| 7 | ejecuta resume(c) continúa dentro | |||
| 8 | termina y sale | (vacío) | (compiten por entrar) | |
| 9a | Si el planificador elige a (robo de condición): | |||
| 9b | Si el planificador elige a : |
En el paso 9, puede entrar antes que . Si consume el recurso que acababa de producir, cuando finalmente entre encontrará que la condición ya no se cumple. Por esta razón, en Mesa/Java es obligatorio comprobar con while.
6. Problema de la cuenta de ahorros
Una cuenta bancaria compartida admite dos operaciones:
depositar(cantidad): ingresa saldo y notifica a los clientes que esperan para retirar.sacar(cantidad): retira fondos únicamente si hay saldo suficiente; en caso contrario, se bloquea. El balance nunca puede ser negativo.
6.1. Solución básica (con posibilidad de adelantamiento)
monitor CuentaAhorros:
integer balance = 0
condition saldo_suficiente
export depositar, sacar
procedure depositar(cantidad):
balance = balance + cantidad
resume(saldo_suficiente) # Despierta a clientes en espera
procedure sacar(cantidad):
while balance < cantidad:
delay(saldo_suficiente)
balance = balance - cantidad
# Si sobra saldo, despierta a otros por si su importe es menor
if balance > 0:
resume(saldo_suficiente)
6.2. Variante sin "colarse": orden FIFO estricto de retirada
Si un cliente solicita 1.000 € y se bloquea, no es justo que un cliente posterior que pide 10 € se cuele y retire dinero antes que él. Para garantizar equidad (fairness):
monitor CuentaAhorrosEstricta:
integer balance = 0
integer turno_actual = 0
integer siguiente_turno = 0
condition cola_turnos
export depositar, sacar
procedure depositar(cantidad):
balance = balance + cantidad
resume(cola_turnos)
procedure sacar(cantidad):
integer mi_turno = siguiente_turno
siguiente_turno = siguiente_turno + 1
# Solo puede retirar si es su turno Y hay balance suficiente
while mi_turno != turno_actual or balance < cantidad:
delay(cola_turnos)
balance = balance - cantidad
turno_actual = turno_actual + 1
resume(cola_turnos) # Pasa el testigo al siguiente en turno
El script cuenta_ahorros.py implementa ambas variantes (CuentaAhorrosInjusta con posibles adelantamientos e inanición frente a CuentaAhorrosJustaFIFO con turnos estrictos) y muestra por consola cómo los turnos ordenan los reintegros equitativamente.
7. Patrones y problemas clásicos con monitores
7.1. Productor-consumidor con búfer acotado
El monitor gestiona un búfer circular de tamaño empleando dos variables de condición:
no_lleno: donde duermen los productores cuando el búfer tiene elementos.no_vacio: donde duermen los consumidores cuando el búfer tiene 0 elementos.
import threading
from collections import deque
class BoundedBuffer:
def __init__(self, capacidad):
self.capacidad = capacidad
self.buffer = deque()
self.mutex = threading.Lock()
self.no_lleno = threading.Condition(self.mutex)
self.no_vacio = threading.Condition(self.mutex)
def producir(self, elemento):
with self.mutex:
while len(self.buffer) == self.capacidad:
self.no_lleno.wait()
self.buffer.append(elemento)
self.no_vacio.notify() # Despierta a un consumidor
def consumir(self):
with self.mutex:
while len(self.buffer) == 0:
self.no_vacio.wait()
elemento = self.buffer.popleft()
self.no_lleno.notify() # Despierta a un productor
return elemento
7.2. Lectores-escritores (prioridad a escritores sin inanición)
Para evitar que una marea continua de lectores provoque la inanición de los escritores, introducimos un contador de escritores en espera (escritores_esperando):
import threading
class ReaderWriterPriority:
def __init__(self):
self.lectores = 0
self.escribiendo = False
self.escritores_esperando = 0
self.mutex = threading.Lock()
self.puede_leer = threading.Condition(self.mutex)
self.puede_escribir = threading.Condition(self.mutex)
def iniciar_lectura(self):
with self.mutex:
# Si hay un escritor escribiendo O escritores en cola, el lector espera
while self.escribiendo or self.escritores_esperando > 0:
self.puede_leer.wait()
self.lectores += 1
def terminar_lectura(self):
with self.mutex:
self.lectores -= 1
if self.lectores == 0:
self.puede_escribir.notify() # El último lector despierta al escritor
def iniciar_escritura(self):
with self.mutex:
self.escritores_esperando += 1
while self.escribiendo or self.lectores > 0:
self.puede_escribir.wait()
self.escritores_esperando -= 1
self.escribiendo = True
def terminar_escritura(self):
with self.mutex:
self.escribiendo = False
# Preferencia a escritores si los hay; si no, despierta a los lectores
if self.escritores_esperando > 0:
self.puede_escribir.notify()
else:
self.puede_leer.notify_all()
7.3. Los filósofos comensales (adquisición atómica de palillos)
Con monitores podemos resolver el problema de los filósofos eliminando el interbloqueo de raíz mediante la adquisición atómica: un filósofo solo toma sus palillos si ambos están libres simultáneamente.
import threading
class DiningPhilosophers:
PENSANDO = 0
HAMBRIENTO = 1
COMIENDO = 2
def __init__(self, n=5):
self.n = n
self.estado = [self.PENSANDO] * n
self.mutex = threading.Lock()
self.puede_comer = [threading.Condition(self.mutex) for _ in range(n)]
def _comprobar(self, i):
izq = (i - 1) % self.n
der = (i + 1) % self.n
# Solo come si tiene hambre y ninguno de sus dos vecinos está comiendo
if (self.estado[i] == self.HAMBRIENTO and
self.estado[izq] != self.COMIENDO and
self.estado[der] != self.COMIENDO):
self.estado[i] = self.COMIENDO
self.puede_comer[i].notify()
def coger_palillos(self, i):
with self.mutex:
self.estado[i] = self.HAMBRIENTO
self._comprobar(i)
while self.estado[i] != self.COMIENDO:
self.puede_comer[i].wait()
def soltar_palillos(self, i):
with self.mutex:
self.estado[i] = self.PENSANDO
# Comprueba si sus vecinos pueden comer ahora que ha liberado los palillos
self._comprobar((i - 1) % self.n)
self._comprobar((i + 1) % self.n)
7.4. Barrera de sincronización para hilos
Una barrera detiene a todos los hilos concurrentes hasta que el último de ellos haya alcanzado el punto de encuentro:
import threading
class Barrier:
def __init__(self, total_hilos):
self.total = total_hilos
self.llegados = 0
self.mutex = threading.Lock()
self.todos_llegaron = threading.Condition(self.mutex)
def esperar(self):
with self.mutex:
self.llegados += 1
if self.llegados == self.total:
self.llegados = 0 # Reutilizable para la siguiente fase
self.todos_llegaron.notify_all()
else:
self.todos_llegaron.wait()
7.5. El problema de la olla de la tribu
Una tribu salvaje cena comunitariamente de una gran olla que contiene como máximo raciones. Cuando un salvaje tiene hambre, se sirve de la olla; si está vacía, despierta al cocinero y espera a que este la llene por completo. Solo se debe despertar al cocinero cuando la olla llega a 0 raciones.
monitor OllaTribu:
integer raciones = M
integer MAX_RACIONES = M
condition olla_vacia # Cola donde duerme el cocinero
condition olla_con_comida # Cola donde duermen los salvajes hambrientos
export servirse_porcion, rellenar_olla
procedure servirse_porcion():
while raciones == 0:
resume(olla_vacia) # Despierta al cocinero
delay(olla_con_comida) # El salvaje espera a que se cocine
raciones = raciones - 1
# El salvaje se sirve y come su porción
procedure rellenar_olla():
while raciones > 0:
delay(olla_vacia) # El cocinero duerme si aún queda comida
raciones = MAX_RACIONES # Cocina un nuevo festín de M raciones
resumeAll(olla_con_comida) # Despierta a los salvajes hambrientos
El script olla_tribu.py implementa el monitor de la tribu con hilos salvajes concurrentes y un hilo cocinero, simulando las comidas comunales y los avisos cuando la olla se vacía.
8. Implementación de monitores en entornos reales
8.1. En POSIX Threads (pthread en C)
En sistemas basados en Unix y estándares POSIX (pthread.h), un monitor se construye combinando un cerrojo mutex (pthread_mutex_t) con una o varias variables de condición (pthread_cond_t):
#include <pthread.h>
typedef struct {
int contador;
pthread_mutex_t cerrojo;
pthread_cond_t condicion;
} MonitorEjemplo;
void monitor_init(MonitorEjemplo *m) {
m->contador = 0;
pthread_mutex_init(&m->cerrojo, NULL);
pthread_cond_init(&m->condicion, NULL);
}
void monitor_esperar_par(MonitorEjemplo *m) {
pthread_mutex_lock(&m->cerrojo);
// Obligatorio bucle while (política Mesa / Signal-and-Continue)
while ((m->contador % 2) != 0) {
// Libera el mutex atómicamente y duerme en la condición
pthread_cond_wait(&m->condicion, &m->cerrojo);
}
// Sección crítica con invariante garantizado
m->contador++;
pthread_mutex_unlock(&m->cerrojo);
}
void monitor_incrementar(MonitorEjemplo *m) {
pthread_mutex_lock(&m->cerrojo);
m->contador++;
// Despierta a un hilo esperando en la condición
pthread_cond_signal(&m->condicion);
pthread_mutex_unlock(&m->cerrojo);
}
8.2. Emulación formal de monitores con semáforos (paso de testigo de Hoare)
Para demostrar que los monitores son computacionalmente equivalentes a los semáforos, C.A.R. Hoare demostró cómo implementar un monitor con disciplina IRR (Señalar y Esperar Urgente) utilizando semáforos binarios mediante la técnica del paso de testigo:
Estructura de datos requerida
- Semáforo
m_mutex(inicializado a 1): Controla la entrada inicial al monitor. - Semáforo
m_next(inicializado a 0): Cola urgente o de cortesía para los hilos que acaban de ejecutarresume()y deben ceder el monitor temporalmente. - Entero
m_next_count(inicializado a 0): Número de hilos esperando en la cola urgente. - Por cada variable de condición :
- Semáforo
m_x_sem(inicializado a 0). - Entero
m_x_count(inicializado a 0): número de hilos esperando en la condición .
- Semáforo
Esqueleto de cada procedimiento público del monitor
wait(m_mutex); // 1. Adquiere el monitor en exclusión mutua
// --- CUERPO DEL PROCEDIMIENTO ---
// 2. Protocolo de salida (Da preferencia estricta a la cola urgente):
if (m_next_count > 0) {
signal(m_next); // Cede el testigo al hilo de la cola urgente
} else {
signal(m_mutex); // Si no hay nadie en cola urgente, libera la entrada
}
Operaciones sobre la variable de condición
void delay(x) {
m_x_count++;
// Al bloquearse, debe ceder el monitor a alguien:
if (m_next_count > 0) {
signal(m_next); // Prioridad a la cola urgente
} else {
signal(m_mutex); // Si no hay nadie urgente, libera la entrada general
}
wait(m_x_sem); // El hilo se suspende en la condición x
m_x_count--;
}
void resume(x) {
if (m_x_count > 0) {
m_next_count++;
signal(m_x_sem); // Despierta al hilo esperando en la condición x
wait(m_next); // El señalizador se suspende de inmediato en la cola urgente
m_next_count--;
}
// Si m_x_count == 0, resume no hace nada (sin memoria)
}
Obsérvese que cuando un hilo hace signal(m_x_sem), no ejecuta signal(m_mutex). Pasa directamente el permiso de ejecución (el "testigo") al hilo que despierta en m_x_sem, y él mismo se suspende en wait(m_next).
El número de hilos activos dentro del código del monitor es siempre estrictamente 1, garantizando la política IRR de Hoare de forma matemáticamente exacta.
9. Código fuente disponible
Todos los ejemplos analizados a lo largo de este tema se encuentran disponibles para su consulta, descarga y ejecución:
producer-consumer.py: Productor-consumidor con búfer acotado y variablesCondition.rw_lock.py: Cerrojo Lectores-Escritores sin inanición.philosophers.py: Filósofos comensales con adquisición atómica de palillos.barrier.py: Barrera de sincronización reusable para hilos.cuenta_ahorros.py: Cuenta de ahorros comparando inanición frente a turnos FIFO estrictos.olla_tribu.py: Problema de los salvajes y el cocinero con la olla comunal.MonitorPosix/: Implementación completa en C de una cola con prioridad (delay(c, p)) mediantepthread_mutex_typthread_cond_t.