Primitivas de sincronización derivadas de los Sistemas Operativos/Semáforos, pensadas para resolver la Sistemas Operativos/Concurrencia de forma más simple y menos propensa a errores.
Un mutex es una versión simplificada del semáforo, sin capacidad de contar. Tiene dos estados: abierto o cerrado.
mutex_lock: si está abierto, lo cierra y el hilo entra a la región crítica. Si está cerrado, el hilo se bloquea.mutex_unlock: lo abre; si hay hilos bloqueados, se elige uno al azar.Implementación de Tanenbaum para hilos en espacio de usuario (con TSL):
mutex_lock:
TSL REGISTER,MUTEX
CMP REGISTER,0
JZE ok | si era 0, estaba abierto: regresar
CALL thread_yield | ocupado: planificar otro hilo
JMP mutex_lock
ok: RET
mutex_unlock:
MOVE MUTEX,0
RET
La diferencia con enter_region es que no hace espera ocupada: cede la CPU.
Un futex (fast user space mutex) evita entrar al Sistemas Operativos/Kernel cuando no hay contención: el candado se toma con una operación atómica en espacio de usuario y sólo se llama al kernel para bloquearse si está ocupado.
| Llamada | Descripción |
|---|---|
pthread_mutex_init / destroy |
Crear / destruir mutex |
pthread_mutex_lock |
Adquirir o bloquearse |
pthread_mutex_trylock |
Adquirir o fallar sin bloquearse |
pthread_mutex_unlock |
Liberar |
Permiten que un hilo se bloquee hasta que se cumpla una condición. Se usan siempre junto con un mutex.
pthread_cond_wait(&cond, &mutex): libera el mutex atómicamente y se bloquea.pthread_cond_signal(&cond): despierta a un hilo.pthread_cond_broadcast(&cond): despierta a todos.A diferencia de los semáforos, no tienen memoria: si se señala una variable de condición sin nadie esperando, la señal se pierde. Por eso se espera siempre dentro de un while que revisa la condición.
Propuestos por Hoare (1974) y Brinch Hansen (1975). Un monitor es una colección de procedimientos, variables y estructuras de datos agrupados en un módulo especial, con una propiedad clave:
Sólo un proceso puede estar activo dentro del monitor en un instante dado.
La exclusión mutua la garantiza el compilador, no el programador, eliminando los errores de orden típicos de los semáforos.
Dentro del monitor se usan variables de condición con wait y signal. Ante un signal:
signal debe ser la última instrucción del procedimiento.while).monitor ProducerConsumer
condition full, empty;
integer count;
procedure insert(item: integer);
begin
if count = N then wait(full);
insert_item(item);
count := count + 1;
if count = 1 then signal(empty)
end;
function remove: integer;
begin
if count = 0 then wait(empty);
remove = remove_item;
count := count - 1;
if count = N - 1 then signal(full)
end;
count := 0;
end monitor;
Java soporta monitores con synchronized en métodos, más wait(), notify() y notifyAll().
Monitores y semáforos asumen memoria compartida. En sistemas distribuidos se usa paso de mensajes.