← Concurrencia y Sincronización Bloque 6 · Variables de condición y monitores · Tema 27 de 47

Tema 27

Monitores

Un monitor es una abstracción de alto nivel que encapsula datos compartidos y las operaciones que los manipulan, garantizando la exclusión mutua automáticamente. En lugar de que el programador bloquee y desbloquee un mutex manualmente, el monitor lo hace por él. En este tema verás qué es un monitor, cómo se diferencia de un mutex, la semántica de Hoare vs. Mesa, y cómo se implementa en lenguajes que lo soportan nativamente.

1. Qué es un monitor

Un monitor es una abstracción de sincronización que encapsula datos compartidos y las operaciones (métodos) que los manipulan, garantizando que solo un hilo a la vez puede ejecutar cualquier operación del monitor. La exclusión mutua es implícita: el programador no necesita bloquear ni desbloquear un mutex manualmente.

El monitor es una construcción de alto nivel que combina datos y sincronización en una sola abstracción. Es como un objeto (en programación orientada a objetos) que es thread-safe por construcción.

La idea es simple: si un monitor encapsula un contador, todos los métodos del monitor (incrementar, decrementar, leer) se ejecutan de forma mutuamente excluyente. El programador solo escribe la lógica; el monitor se encarga de la sincronización.

Monitor = datos + operaciones + exclusión mutua implícita

Un monitor es como una clase en POO, pero con la garantía de que solo un hilo ejecuta un método a la vez. La exclusión mutua es automática, no manual.

2. Origen: Hoare y Brinch Hansen

El concepto de monitor fue propuesto independientemente por C.A.R. Hoare y Per Brinch Hansen en 1974. Hoare publicó «Monitors: An Operating System Structuring Concept» y Brinch Hansen publicó «Structured Multiprogramming».

Hoare definió el monitor como una construcción del lenguaje de programación, no como una biblioteca. En un lenguaje con monitores, el compilador genera automáticamente el código de sincronización (lock/unlock) alrededor de los métodos del monitor.

Brinch Hansen implementó monitores en el lenguaje Concurrent Pascal, que fue uno de los primeros lenguajes con soporte para concurrencia.

3. Estructura de un monitor

Un monitor tiene esta estructura:

estructura de un monitor
monitor NombreMonitor {
    /* Variables compartidas (privadas). */
    int dato;
    condition cond;

    /* Métodos (operaciones). */
    void metodo1() {
        /* exclusión mutua implícita */
        /* ... */
    }

    int metodo2() {
        /* exclusión mutua implícita */
        /* ... */
        return dato;
    }
}

Las características son:

4. Exclusión mutua implícita

La exclusión mutua implícita es la propiedad más importante del monitor: el programador no necesita bloquear ni desbloquear un mutex. El monitor lo hace automáticamente.

Internamente, el monitor tiene un mutex (o lock) que se adquiere al entrar a un método y se libera al salir. El programador solo escribe la lógica del método; el monitor se encarga de la sincronización.

Esto elimina una fuente importante de errores: olvidar el unlock, bloquear dos veces, etc. El programador no puede cometer estos errores porque no maneja el lock manualmente.

El monitor protege automáticamente

El programador no puede olvidar el unlock porque no hay unlock manual. El monitor lo hace automáticamente al salir del método. Esto elimina una clase entera de errores.

5. Variables de condición en monitores

Los monitores usan variables de condición para esperar a que se cumpla una condición. A diferencia de los semáforos, las variables de condición de un monitor tienen operaciones wait y signal (o notify).

En un monitor, wait hace tres cosas:

  1. Libera el mutex del monitor. Otros hilos pueden entrar. liberar
  2. Se bloquea. El hilo se duerme en la cola de la condición. bloquear
  3. Al despertar, re-adquiere el mutex. Antes de continuar, el hilo bloquea el mutex de nuevo. re-adquirir

signal (o notify) despierta a un hilo bloqueado en la condición. broadcast (o notifyAll) despierta a todos.

6. Semántica de Hoare vs. Mesa

Hay dos semánticas para las variables de condición en monitores, propuestas por Hoare y Mesa:

SemánticaQué ocurre al hacer signalVentajaDesventaja
HoareEl hilo señalador se bloquea y el hilo señalado se ejecuta inmediatamente (con el mutex).La condición se cumple cuando el hilo señalado se ejecuta. Se puede usar if.Más cambios de contexto.
MesaEl hilo señalador continúa ejecutándose. El hilo señalado se despierta pero debe esperar el mutex.Menos cambios de contexto.La condición puede no cumplirse cuando el hilo señalado se ejecuta (se necesita while).

7. Semántica de Hoare

En la semántica de Hoare, cuando un hilo hace signal, el hilo señalador se bloquea y el hilo señalado se ejecuta inmediatamente con el mutex. Esto garantiza que la condición se cumple cuando el hilo señalado se ejecuta.

Ventajas:

Desventajas:

8. Semántica de Mesa

En la semántica de Mesa, cuando un hilo hace signal, el hilo señalador continúa ejecutándose. El hilo señalado se despierta pero debe esperar a que el mutex se libere antes de continuar.

Ventajas:

Desventajas:

Mesa es la semántica usada en la mayoría de los sistemas modernos (Java, C#, POSIX) porque es más eficiente.

9. Comparación

AspectoHoareMesa
Al hacer signalEl señalador se bloquea, el señalado se ejecuta.El señalador continúa, el señalado espera el mutex.
Condición al ejecutarSe cumple.Puede no cumplirse.
Bucleif es suficiente.while es necesario.
Cambios de contextoMás.Menos.
Uso realPoco usado.Java, C#, POSIX.

10. Monitores en lenguajes de programación

Los monitores se implementan de diferentes formas en distintos lenguajes:

LenguajeSoporte de monitoresSemántica
Javasynchronized en métodos o bloques. wait(), notify(), notifyAll().Mesa
C#lock statement. Monitor.Wait(), Monitor.Pulse(), Monitor.PulseAll().Mesa
Concurrent PascalMonitor nativo del lenguaje.Hoare
Modula-3Monitor nativo con mutex y condition.Mesa
C (pthreads)No nativo. Se implementa con mutex + variables de condición.Mesa

Java y C# son los lenguajes más usados que soportan monitores nativamente. En C, los monitores se implementan manualmente con mutex y variables de condición.

11. Monitores en Java

En Java, todo objeto es un monitor potencial. El método synchronized adquiere el lock del objeto:

Java · monitor
public class Cuenta {
    private int saldo = 0;

    public synchronized void depositar(int monto) {
        saldo += monto;
        notify();  /* señalar a un hilo que espera */
    }

    public synchronized void retirar(int monto) throws InterruptedException {
        while (saldo < monto) {
            wait();  /* esperar a que haya suficiente saldo */
        }
        saldo -= monto;
    }
}

En Java, synchronized adquiere el lock del objeto. wait() libera el lock y bloquea. notify() despierta a un hilo. notifyAll() despierta a todos.

La semántica es Mesa: notify() no transfiere el lock inmediatamente. El hilo despertado debe esperar a que el lock se libere.

12. Monitores en C#

En C#, el statement lock adquiere el lock de un objeto:

C# · monitor
public class Cuenta {
    private int saldo = 0;
    private readonly object lock = new object();

    public void Depositar(int monto) {
        lock (lock) {
            saldo += monto;
            Monitor.Pulse(lock);  /* señalar */
        }
    }

    public void Retirar(int monto) {
        lock (lock) {
            while (saldo < monto) {
                Monitor.Wait(lock);  /* esperar */
            }
            saldo -= monto;
        }
    }
}

En C#, lock es equivalente a Monitor.Enter/Exit. Monitor.Wait libera el lock y bloquea. Monitor.Pulse despierta a un hilo. Monitor.PulseAll despierta a todos.

13. Ventajas

14. Desventajas

15. Simulador interactivo

Este simulador muestra cómo un monitor protege una cuenta bancaria:

Simulador · monitor (cuenta bancaria)

MedidaValor
Saldo inicial0
Saldo esperado
Saldo obtenido
Diferencia

16. Resumen

17. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué es un monitor?

Una abstracción que encapsula datos y operaciones con exclusión mutua implícita.

2. ¿Quién propuso el concepto de monitor?

C.A.R. Hoare y Per Brinch Hansen, en 1974.

3. ¿Qué es la exclusión mutua implícita?

El monitor garantiza automáticamente que solo un hilo ejecuta un método a la vez.

4. ¿Qué ventaja tiene la exclusión mutua implícita?

El programador no puede olvidar el unlock ni bloquear dos veces.

5. ¿Qué es la semántica de Hoare?

Al hacer signal, el señalador se bloquea y el señalado se ejecuta inmediatamente.

6. ¿Qué es la semántica de Mesa?

Al hacer signal, el señalador continúa y el señalado espera el mutex.

7. ¿Qué semántica usa Java?

Mesa.

8. ¿Qué semántica usa POSIX?

Mesa.

9. ¿Se pueden implementar monitores en C?

Sí, con mutex y variables de condición.

10. ¿Cuál es la desventaja principal de los monitores?

Son menos flexibles que los mutex (no se puede usar trylock fácilmente).

Para practicar

  1. Implementa un monitor en C con mutex y variables de condición.
  2. Escribe una cuenta bancaria como monitor en Java.
  3. Compara la semántica de Hoare y Mesa con un ejemplo.
  4. Explica por qué los monitores reducen errores.
  5. Investiga cómo se implementan los monitores en el núcleo de Linux.
  6. Implementa un monitor para una cola acotada.