Está en la página 1de 13

SO2 Sistemas operativos II Sincronización de hilos.

Semáforos

„ Índice:
Unidad Temática 8 – Soluciones a nivel del sistema operativo
– Semáforos
– Problemas del uso inadecuado de semáforos
Sincronización de hilos.

S02 Sistemas operativos II


– Semáforos en POSIX
Semáforos – Sincronización de pthreads
„ Mutexes
„ Variables condición

Equipo docente de „ Bibliografía:


– Kay A. Robbins, Steven Robbins.“UNIX Programación Práctica”.
“Sistemas Operativos DISCA/DSIC”
„ Capítulos 8 y 10
Universidad Politécnica de Valencia – A. Silberschatz, P.B. Galvin: “Sistemas Operativos”, 5ª edición, Addison-Wesley.
„ Capítulos 4 y 6.

Indice Soluciones a nivel del S.O.

„ Las soluciones básicas vistas hasta ahora


„ Soluciones a nivel del sistema operativo – No involucran al sistema operativo
„ Semáforos – Tienen el problema de la espera activa

„ Problemas del uso inadecuado de semáforos – Resuelven la exclusión mutua, pero no resuelven el problema de cómo detener
S02 Sistemas operativos II

S02 Sistemas operativos II


a los procesos dentro de una sección crítica
„ Semáforos en POSIX „ Recordar ejemplo de productor/consumidor con “test and set”.
„ Sincronización de pthreads
– Mutexes „ En qué nos puede ayudar el sistema operativo
– Variables condición – El S.O. puede proporcionarnos primitivas específicas para resolver el problema
de la sección crítica
„ Se implementan como llamadas al sistema
„ Internamente, el núcleo puede utilizar instrucciones privilegiadas (como inhabilitar
interrupciones) para implementar las primitivas sin condiciones de carrera
– El S.O. puede detener procesos/hilos de forma eficiente, marcándolos como
suspendidos en su PCB
„ No se produce espera activa
Indice Definición de semáforos

„ Los semáforos
„ Soluciones a nivel del sistema operativo – Constituyen una de las primeras soluciones a la sección crítica a nivel del S.O.
„ Semáforos – Fueron definidos originalmente por Dijkstra en 1965

„ Problemas del uso inadecuado de semáforos


S02 Sistemas operativos II

S02 Sistemas operativos II


„ Un semáforo
„ Semáforos en POSIX
– Conceptualmente, puede entenderse como un valor entero que admite
„ Sincronización de pthreads operaciones de incremento y decremento
– Mutexes „ El decremento puede suspender al proceso que lo invoca
– Variables condición „ El incremento puede despertar a otro proceso, previamente suspendido

– Es un tipo de dato que el sistema operativo pone a disposición de los procesos


de usuario
„ Se declara como una variable de tipo “semáforo”, indicando su valor entero inicial
„ Posee dos operaciones de acceso, implementadas como llamadas al sistema
– Decremento ó P
– Incremento ó V

Definición de semáforos Implementación de semáforos


„ Especificación de las operaciones de acceso „ Definición del tipo de dato semáforo
(1) Declaración e inicialización – Los semáforos son tipos abstractos de datos, implementados dentro del sistema
S : semaforo(N); operativo, y cuyas operaciones de acceso son llamadas al sistema
– Declara el semaforo “S” con un valor inicial de “N”
S02 Sistemas operativos II

S02 Sistemas operativos II


– “N” tiene que ser mayor o igual que cero
– Una posible implementación del tipo sería como un registro, con dos campos:
„ Un contador entero
(2) Decremento „ Una cola de punteros a los PCBs de los procesos que están en cada momento
P(S); suspendidos en el semáforo
S = S - 1;
¡¡atómicamente!!
si S < 0 entonces suspender(S);
„ Definición posible en C:
(3) Incremento
V(S); typdef struct semaforo {
S = S + 1; int contador;
¡¡atómicamente!!
si S <= 0 entonces despertar(S); PCB *cola;
} semaforo;
– Notas:
„ suspender(S): suspende al proceso invocante en una cola asociada a “S”
„ despertar(S): extrae un proceso de la cola de “S” y lo despierta (pasa a preparado)
Implementación de semáforos Implementación de semáforos
Llamadas al Sistema
„ Estructuras de datos necesarias dentro del sistema operativo: „ Operaciones: relacionadas con semáforos
– Un vector de semáforos
semáforo(valor) P(S) V(S) Activación del
– La cola de cada semáforo puede construirse como una lista de punteros a los
Sistema Operativo
PCBs
S02 Sistemas operativos II

S02 Sistemas operativos II


Guardar parte del contexto de usuario, para realizar cambio de modo de ejecución (usuario->núcleo)

Tabla de procesos
i-1 i i+1
Reservar un semáforo S; S.contador = S.contador-1; S.contador= S.contador+1;
contador = -3 S.contador = valor; si S.contador < 0 si S.contador <= 0
... ... entonces
PCB 8 entonces
cola Pasar a suspendido al p=extraer proceso de S.cola;
proceso en ejecución;
Si S.contador Pasar p a preparado
Insertarlo en S.cola;
Estado: SUSP
Evento: Sem-i si no
Semáforo “i” PCB 4 PCB 2
...
P. siguiente Planificador:
Realizar cambio de modo de ejecución (núcleo -> usuario).
selección del próximo
Recuperar contexto de usuario.
proceso

Solución a la S.C. con semáforos Semáforos como mecanismo de sincronización

„ Para resolver el problema de la sección crítica „ Los semáforos no sólo sirven para resolver el problema de la sección crítica
– Definimos un semáforo compartido, denominado “mutex”, inicializado a 1 „ Pueden servir para sincronizar procesos en general, por ejemplo:
– Código de los N procesos/hilos: (1) Establecer un cierto orden (precedencia) en la ejecución de zonas de código
de diferentes procesos/hilos
S02 Sistemas operativos II

S02 Sistemas operativos II


void *hilo_i(void *p) {
„ Supongamos dos hilos, “hilo1” e “hilo2”
while(1) { „ Queremos que “hilo1” ejecute la función “F1” antes que “hilo2” ejecute la función “F2”;
P(mutex); „ Definimos un semáforo compartido “sinc”, inicializado a cero

/* Sección crítica */
void *hilo1(void *p) { void *hilo2(void *p) {
V(mutex);
... ...
/* Sección restante */ Esta solución cumple: F1; P(sinc);
(a) Exclusión mutua V(sinc); F2;
} ... ...
(b) Progreso
} } }
(c) Espera limitada, si la cola
del semáforo es FIFO
Semáforos como mecanismo de sincronización Ejemplos

„ ... y también pueden servir para: „ Ejemplo 1: Productor/Consumidor (1)


(2) Establecer que un máximo número de procesos/hilos pueden pasar por un – Primera solución: conseguir la exclusión mutua
cierto punto del código „ Definimos un semáforo “mutex” inicializado a uno
„ Tenemos un código que ejecutan concurrentemente muchos hilos
void *func_prod(void *p) { void *func_cons(void *p) {
S02 Sistemas operativos II

S02 Sistemas operativos II


„ Queremos que, como mucho, 5 hilos pasen por un determinado lugar del código, int item; int item;
donde se llama a la función “F”
„ Definimos un semáforo compartido “max_5”, inicializado a cinco while(1) { while(1) {
item = producir(); P(mutex);
P(mutex); while (contador == 0)
void *hilo_i(void *p) { while (contador == N) /*bucle vacio*/ ;
/*bucle vacio*/ ; item = buffer[salida];
... buffer[entrada] = item; salida = (salida + 1) % N;
P(max_5); entrada = (entrada + 1) % N; contador = contador - 1;
F; contador = contador + 1; V(mutex);
V(max_5); V(mutex); consumir(item);
... } }
} } }

„ Esta solución no funciona, porque un productor o consumidor que haya cerrado


“mutex” y se quede en el bucle “while”, deja a todos los demás hilos bloqueados!!

Ejemplos Ejemplos

„ Ejemplo 1: Productor/Consumidor (2) „ Ejemplo 2: Lectores/Escritores


– Segunda solución: conseguir la suspensión de productores y consumidores – Definición del problema
LL LL
GO L L
„ Hay un conjunto de datos comunes (un
„ Definimos dos semáforos más
fichero, una base de datos, etc.)
– “lleno”, inicializado a cero, suspende a los consumidores cuando esté vacío „ Los procesos lectores, acceden a los
S02 Sistemas operativos II

S02 Sistemas operativos II


– “vacio”, inicializado a N, suspende a los productores cuando el buffer está lleno datos en modo de sólo lectura
„ Los procesos escritores, acceden a los
void *func_prod(void *p) { void *func_cons(void *p) { datos en modo lectura-escritura
int item; int item; STOP E E
LL LL
while(1) { while(1) { – Reglas de corrección que tienen que
item = producir(); P(lleno); cumplirse:
P(vacio); P(mutex); „ Diversos lectores pueden leer
P(mutex); item = buffer[salida]; concurrentemente.
buffer[entrada] = item; salida = (salida + 1) % N; „ Los escritores se excluyen mutuamente
entrada = (entrada + 1) % N; contador = contador - 1; entre ellos
STOP E E
contador = contador + 1; V(mutex); Los escritores se excluyen mutuamente
„
E
V(mutex); V(vacio); con los lectores. STOP
L L
V(lleno); consumir(item);
} }
} }
Ejemplos Ejemplos
„ Ejemplo Lectores/Escritores: Servicio de chat (conversación interactiva) „ Ejemplo 2: Lectores/Escritores
– Recurso común – Existen tres variantes:
„ La conversación que va manteniendo un grupo de usuarios se almacena en un (1) Prioridad a los lectores
fichero. – Si hay lectores leyendo y
„ Este fichero es accedido por el grupo de usuarios en modo lectura (para visualizar la – escritores esperando, entonces STOP E E
S02 Sistemas operativos II

S02 Sistemas operativos II


conversación) o en escritura (para añadir una frase).
– un nuevo lector tiene preferencia sobre
– Lectores el escritor que espera.
L L
„ Cualquier usuario que se conecta al chat puede visualizar la conversación Î Hay inanición para los escritores, si no
– es un lector del fichero de la conversación. paran de llegar lectores
„ Varios lectores puede acceder al fichero simultáneamente (en modo lectura).
– Escritores (2) Prioridad a los escritores
„ Cuando un miembro quiere añadir una frase a la conversación se convierte en – Si hay escritores esperando,
escritor. – éstos siempre tienen preferencia sobre STOP L
„ Sólo puede escribir un usuario simultáneamente (exclusión mutua). Es decir, la nuevos lectores que lleguen
escritura en el fichero tiene que ser secuencial Î Hay inanición para los lectores, si no L STOP E E E
– sino las frases aparecerían intercaladas. paran de llegar escritores
„ Cuando se está añadiendo una frase los lectores no pueden acceder al fichero.
(3) Igual prioridad (sin inanición) para lectores y
escritores

Ejemplos Ejemplos

„ Ejemplo 2: Lectores/Escritores. Solución a la primera variante „ Ejemplo 2: Lectores/Escritores. Solución a la segunda variante
– Variables compartidas: – Variables compartidas:
semaforo mutex(1), esc(1); semaforo mutex(1), mutex2(1), esc(1), turno(1);
int nlectores = 0; int nlectores = 0, nescritores = 0;
S02 Sistemas operativos II

S02 Sistemas operativos II


– Código de lectores y escritores: – Código de lectores y escritores:
void *func_escritor(void *p) {
void *func_lector(void *p) { void *func_escritor(void *p) { void *func_lector(void *p) { while(1) {
while(1) { while(1) { while(1) { P(mutex2);
P(mutex); P(esc); P(turno); V(turno); nescritores++;
nlectores = nlectores + 1; escribir(); P(mutex); if (nescritores==1)P(turno);
if (nlectores == 1) P(esc); V(esc); nlectores++; V(mutex2);
V(mutex); } if (nlectores == 1) P(esc); P(esc);
leer(); } V(mutex); escribir();
P(mutex); leer(); V(esc);
nlectores = nlectores - 1; P(mutex); P(mutex2);
if (nlectores == 0) V(esc); nlectores--; nescritores--;
V(mutex); if (nlectores == 0) V(esc); if (nescritores==0)V(turno);
} V(mutex); V(mutex2);
} } }
} }
Indice Problemas del uso inadecuado de semáforos

„ El uso inadecuado de semaforos


„ Soluciones a nivel del sistema operativo – Puede dejar a un conjunto de procesos/hilos bloqueados
„ Semáforos – Si el único proceso/hilo que puede despertar a otro(s) nunca lo hace, o también
se suspende, todos pueden quedarse suspendidos para siempre
„ Problemas del uso inadecuado de semáforos
S02 Sistemas operativos II

S02 Sistemas operativos II


„ La más grave de estas situaciones se denomina interbloqueo
„ Semáforos en POSIX
– Un interbloqueo es una situación en la que un conjunto de procesos/hilos se
„ Sincronización de pthreads quedan suspendidos, cada uno de ellos esperando a que otro del grupo lo
– Mutexes despierte
– Variables condición – Ejemplo:
„ Dos hilos, “hilo1” e “hilo2” manipulan dos semáforos (S1 y S2), inicializados a uno
„ Código:
void *hilo1(void *p) { void *hilo2(void *p) {
... ...
P(S1); P(S2);
P(S2); P(S1);
... ...
} }

Indice Semáforos en POSIX


„ POSIX.1b introdujo el tipo de variable semáforo sem_t
„ Soluciones a nivel del sistema operativo
#include <semaphore.h>
„ Semáforos
sem_t sem;
„ Problemas del uso inadecuado de semáforos
S02 Sistemas operativos II

S02 Sistemas operativos II


– Los semáforos se pueden compartir entre procesos y pueden ser accedidos por
„ Semáforos en POSIX parte de todos los hilos del proceso. Los semáforos se heredan padre-hijo igual
que los descriptores de fichero.
„ Sincronización de pthreads
„ Las operaciones que soporta son:
– Mutexes
– int sem_init(sem_t *sem, int pshared, unsigned int value);
– Variables condición
– int sem_destroy(sem_t *sem);
Operación
– int sem_wait(sem_t *sem); P(sem)
– int sem_trywait(sem_t *sem);
– int sem_post(sem_t *sem); Operación
V(sem)
– int sem_getvalue(sem_t *sem, int *sval);
Semáforos POSIX. Ejemplo. Indice
#include <semaphore.h> Productor/Consumidor
sem_t mutex, lleno, vacio;
„ Soluciones a nivel del sistema operativo
void *func_prod(void *p) { void *func_cons(void *p) {
int item; int item; „ Semáforos
„ Problemas del uso inadecuado de semáforos
S02 Sistemas operativos II

S02 Sistemas operativos II


while(1) { while(1) {
item = producir(); sem_wait(&lleno); „ Semáforos en POSIX
sem_wait(&vacio); sem_wait(&mutex);
sem_wait(&mutex); item = buffer[salida]; „ Sincronización de pthreads
buffer[entrada] = item; salida = (salida + 1) % N;
entrada = (entrada + 1) % N; contador = contador - 1; – Mutexes
contador = contador + 1; sem_post(&mutex); – Variables condición
sem_post(&mutex); sem_post(&vacio);
sem_post(&lleno); consumir(item);
} }
} }

sem_init(&mutex,0,1);
sem_init(&vacio,0,N);
sem_init(&lleno,0,0);

Sincronización de pthreads Indice

„ POSIX.1c ofrece dos objetos de sincronización:


„ Soluciones a nivel del sistema operativo
– “mutexes”: para la gestión de exclusión mutua. Son como semáforos que
sólo pueden tomar valor inicial 1. „ Semáforos
– “variables condition”: para la espera de sucesos de duración ilimitada. Los „ Problemas del uso inadecuado de semáforos
S02 Sistemas operativos II

S02 Sistemas operativos II


hilos se suspenden voluntariamente en estas variables.
„ Semáforos en POSIX
– Los mutexes y las variables condición se pueden usar en todos los hilos del
proceso que los crea. Están pensados para sincronizar hilos de un mismo „ Sincronización de pthreads
proceso, aunque excepcionalmente se pueden compartir entre procesos. – Mutexes
– Variables condición
„ Como veremos, POSIX impone además un estilo de programación
– Las variables de condición se usan en combinación con los mutexes.
– Su uso resulta más intuitivo que el de los semáforos.
Sincronizació
Sincronización de pthreads.
pthreads. Mutexes Sincronizació
Sincronización de pthreads.
pthreads. Mutexes
„ Los mutex:
– Son mecanismos de sincronización a nivel de hilos (threads) „ Clasificación de llamadas POSIX para gestión de mutex:
– Se utilizan sólo para garantizar la exclusión mutua. – Creación y destrucción:
– Conceptualmente, funcionan como un cerrojo: „ pthread_mutex_init
– Existen dos operaciones básicas de acceso (funciones): cierre y apertura „ pthread_mutex_destroy
S02 Sistemas operativos II

S02 Sistemas operativos II


– Cada mutex posee:
„ estado: dos posibles estados internos: abierto y cerrado
– Inicialización de los atributos
„ propietario: Un hilo es el propietario del mutex cuando ha ejecutado sobre él una
operación de cierre con éxito. „ pthread_mutexattr_init
„ pthread_mutexattr_destroy
„ Funcionamiento:
„ Modificación/Consulta de valores al atributo: compartición, protocolo, etc.
– Un mutex se crea inicialmente abierto y sin propietario
– Cuando un hilo invoca la operación de cierre Operación
– Cierre y apertura
„ Si el mutex estaba abierto (sin propietario), lo cierra y pasa a ser su propietario de cierre (P)
„ Si el mutex ya estaba cerrado, el hilo invocante se suspende „ pthread_mutex_lock
– Cuando el propietario del hilo invoca la operación de apertura „ pthread_mutex_trylock
Operación
„ Se abre el mutex „ pthread_mutex_unlock de apertura (V)
„ Si existían hilos suspendidos en el mismo, se selecciona uno y se despierta, con lo que
puede cerrar el mismo (y pasa a ser el nuevo propietario)

Sincronizació
Sincronización de pthreads.
pthreads. Mutexes Sincronizació
Sincronización de pthreads.
pthreads. Mutexes

„ Creación/destrucción de mutexes: „ Atributos de creación de mutexes:

int pthread_mutex_init(pthread_mutex_t *mutex,


const pthread_mutexattr_t *attr); int pthread_mutexattr_init(pthread_mutexattr_t *attr);

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);


S02 Sistemas operativos II

S02 Sistemas operativos II


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int pthread_mutex_destroy(pthread_mutex_t *mutex);

donde
donde – Creamos/destruimos los atributos attr con los que luego crearemos el mutex
– Para crear el mutex, tenemos dos opciones: – Esos atributos pueden modificarse mediante funciones específicas.
„ con los atributos attr (previamente creados con phread_mutexattr_init) „ P.e. utilizando la función pthread_mutexattr_setpshared podemos modificar el
„ o con los atributos por defecto (PTHREAD_MUTEX_INITIALIZER) atributo pshared para conseguir que el mutex pueda ser usado por otros procesos

– Para destruir el mutex, se utiliza la función pthread_mutex_destroy


Sincronizació
Sincronización de pthreads.
pthreads. Mutexes Sincronizació
Sincronización de pthreads.
pthreads. Mutexes

„ Cierre y apertura de mutexes: „ Ejemplo: acceso concurrente a una variable por parte de dos hilos
– Código del programa principal:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_unlock(pthread_mutex_t *mutex);
S02 Sistemas operativos II

S02 Sistemas operativos II


int V = 100;
donde
// Código de los hilos (transparencia siguiente)
– lock(trylock) cierra (o intenta cerrar) el mutex
„ Si está abierto, se cierra y el hilo invocante pasa a ser el propietario int main ( ) {
„ Si está cerrado: pthread_t hilo1, hilo2;
pthread_attr_t atributos;
– lock suspende el hilo invocante pthread_attr_init(&atributos);
– trylock devuelve inmediatamente un código de error pthread_create(&hilo1, &atributos, fhilo1, NULL);
pthread_create(&hilo2, &atributos, fhilo2, NULL);
– unlock abre el mutex pthread_join(hilo1,NULL);
„ Si hay hilos suspendidos, selecciona el más prioritario y permite que cierre el mutex pthread_join(hilo2,NULL);
}

Sincronizació
Sincronización de pthreads.
pthreads. Mutexes Indice

„ Ejemplo:
– Código de los hilos: „ Soluciones a nivel del sistema operativo
„ Semáforos
void *hilo1(void *p) { void *hilo2(void *p) {
„ Problemas del uso inadecuado de semáforos
S02 Sistemas operativos II

S02 Sistemas operativos II


int c; int c;

for(c=0; c<1000; c++) { for(c=0; c<1000; c++) { „ Semáforos en POSIX


pthread_mutex_lock(&m); pthread_mutex_lock(&m);
V := V + 1; V := V - 1;
„ Sincronización de pthreads
pthread_mutex_unlock(&m); pthread_mutex_unlock(&m); – Mutexes
} }
– Variables condición
pthread_exit(0); pthread_exit(0);
} }
Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición

„ Existen ocasiones en las que los hilos: „ Por este motivo:


– Deben suspenderse a la espera de que se produzca una condición lógica – La mejor solución es proporcionar un mecanismo general que combine
determinada la suspensión de hilos con los mutex.
– Este mecanismo se denomina variables de condición:
Son un tipo abstracto de datos con tres operaciones básicas:
S02 Sistemas operativos II

S02 Sistemas operativos II


„
– Esta condición lógica suele hacer referencia al estado de recursos (variables)
– “Espera”, “Aviso simple” y “Aviso múltiple”
que se estén compartiendo (ejemplo: condición de buffer lleno ó vacío)
„ Las variables de condición se llaman así por el hecho de que siempre se
usan con una condición, es decir, un predicado. Un hilo prueba un
predicado y llama a “Espera” si el predicado es falso. Cuando otro hilo
– El problema viene cuando la suspensión se lleva a cabo en medio de una
modifica variables que pudieran hacer que se cumpla la condición, activa el
sección de código en exclusión mutua: hilo bloqueado llamando a “Aviso”.
„ El hilo tiene uno (o varios) mutexes cerrados
„ Si el hilo se suspende sin más, los mutexes quedan cerrados y ningún otro hilo puede – Las variables de condición siempre están asociadas a un mutex.
ejecutar su “sección crítica” (a pesar de que él mismo no está ejecutándola) POSIX establece que:
„ la espera debe invocarse desde dentro de una sección crítica, es decir,
cuando el hilo es el propietario de un cierto mutex
„ el aviso debe invocarse desde dentro de una sección crítica, cuando el
hilo es el propietario del mismo mutex asociado a la condición

Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición

„ Especificación de las operaciones de acceso: „ En resumen, POSIX establece que el uso correcto es el siguiente:
– La invocación de espera(c,m) ejecuta una apertura del mutex “m” y – Dentro de una sección crítica, protegida por “mutex”, un cierto hilo (hilo1) comprueba si
se cumple una condición y, en su caso, se suspende en “cond”
suspende al hilo invocante en la variable de condición “c”
„ El hilo tiene que ser el propietario de “m” antes de invocar esta operación
– Dentro de una sección crítica protegida por el mismo “mutex”, otro hilo (hilo2) despierta
S02 Sistemas operativos II

S02 Sistemas operativos II


„ Las acciones de abrir el mutex y suspenderse en la condición se ejecutan de a un hilo (o a todos, con el aviso_múltiple) suspendidos en “cond” si considera que se
forma atómica modifica alguna variable involucarda en el predicado “condición”.
„ Cuando el hilo se despierte posteriormente, realizará automáticamente una
operación de cierre sobre “m” antes de seguir con la ejecución del código tras la hilo1: hilo2:
“espera”
cierre(mutex); cierre(mutex);
– La invocación de aviso simple(c) despierta a un hilo suspendido en “c” ...
si (condición) entonces ...
„ Si no hay hilos suspendidos, la operación no tiene efecto (el aviso se pierde) espera(cond, mutex); //Modifica variables
... //involucradas en “condición”
aviso_simple(cond);
– La invocación de aviso múltiple(c) despierta a todos los hilos suspendidos
apertura(mutex); ...
en “c”
„ Si no hay hilos suspendidos, tampoco tiene efecto apertura(mutex);
Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición

„ Ejemplo (pseudo-código): „ Clasificación de llamadas POSIX para variables condición:


hilo1: hilo2: – Creación y destrucción:
cierre(mutex); cierre(mutex); „ pthread_cond_init
si (condición) entonces // Acceso a variables comunes
„ pthread_cond_destroy
espera(cond, mutex); aviso_simple(cond);
S02 Sistemas operativos II

S02 Sistemas operativos II


//Acceso a variables comunes apertura(mutex); – Inicialización
apertura(mutex); „ pthread_condattr_init
„ pthread_condattr_destroy
„ Asignación de valores al atributo: compartición
Operación
(hilo1) cierre(mutex) – Espera y aviso de espera
(hilo2) cierre(mutex) „ pthread_cond_wait
(hilo1) espera(cond,mutex)+apertura(mutex) „ pthread_cond_timedwait Operación de
(hilo2) cierre(mutex)
„ pthread_cond_signal aviso simple
(hilo2) acceso a variables
(hilo2) aviso_simple(cond) „ pthread_cond_broadcast
(hilo2) apertura(mutex) Operación de
(hilo1) cierre(mutex) aviso múltiple
(hilo1) acceso a variables
(hilo1) apertura(mutex)

Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición

„ Creación/destrucción de variables de condición: „ Atributos de creación de variables de condición:


int pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *attr); int pthread_condattr_init(pthread_condattr_t *attr);

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;


S02 Sistemas operativos II

S02 Sistemas operativos II


int pthread_condattr_destroy(pthread_condattr_t *attr);

int pthread_cond_destroy(pthread_cond_t *cond);

donde
donde – Creamos/destruimos los atributos attr con los que luego crearemos la variable
– Para crear la variable cond, tenemos dos opciones: de condición
„ con los atributos attr (previamente creados con phread_condattr_init) – Esos atributos pueden modificarse mediante funciones específicas.
„ o con los atributos por defecto (PTHREAD_COND_INITIALIZER) „ P.e. utilizando la función pthread_condattr_setpshared podemos modificar el
atributo pshared para conseguir que la condición pueda ser usada por otros procesos

– Para destruir la variable cond, utilizamos la función pthread_cond_destroy


Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición

„ Espera sobre variables de condición: „ Aviso sobre variables de condición:


int pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex); int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_timedwait(pthread_cond_t *cond, int pthread_cond_broadcast(pthread_cond_t *cond);
pthread_mutex_t *mutex,
S02 Sistemas operativos II

S02 Sistemas operativos II


const struct timespec *abstime);
donde
donde – signal selecciona el hilo más prioritario suspendido en cond y lo despierta
– wait atómicamente ejecuta pthread_mutex_unlock sobre mutex y – broadcast despierta todos los hilos que pueda haber suspendidos en cond
suspende al hilo invocante en la variable cond „ Ambas no tienen efecto si no hay hilos suspendidos en la variable
„ Al despertarse, realizará automáticamente un pthread_mutex_lock sobre mutex
– En ambos casos, es recomendable que el hilo invocante sea el propietario del
– timedwait actúa como wait, salvo que el hilo se suspende como mucho mutex asociado a cond en los hilos suspendidos.
hasta que se alcanza el instante de tiempo abstime „ De esta manera se garantiza que, si el hilo que va a suspenderse en un wait
„ Si se alcanza “abstime” antes de que otro hilo ejecute un signal/broadcast comienza su sección crítica antes que el hilo que va a despertarlo, entonces el aviso
sobre la variable de condición, el hilo dormido se despierta, y timedwait devuelve el no se pierde, porque hasta que el primer hilo no se duerme y libera el mutex, el
error ETIMEDOUT segundo no puede alcanzar la instrucción de signal/broadcast.

Sincronizació
Sincronización de pthreads.
pthreads. Variables de condición Sincronizació
Sincronización de pthreads.
pthreads. Ejemplos

„ Cómo se debe programar con variables de condición: „ Ejemplo1: Productor/Consumidor (1)


– Aunque es posible programar así: – Definición de variables globales:

Hilo 1 Hilo 2 #define N 20


int buffer[N];
pthread_mutex_lock(&m); pthread_mutex_lock(&m);
S02 Sistemas operativos II

S02 Sistemas operativos II


int entrada, salida, contador;
if (! condición) ... pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_wait(&c, &m); // Se produce la condición pthread_cond_t lleno = PTHREAD_COND_INITIALIZER;
// Resto de la S.C. pthread_cond_signal(&c); pthread_cond_t vacio = PTHREAD_COND_INITIALIZER;
pthread_mutex_unlock(&m); pthread_mutex_unlock(&m);

– Programa principal:
– Cuando hay más de un hilo del tipo “Hilo 1”, se recomienda: int main ( ) {
pthread_t prod, cons;
Hilo 1 Hilo 2
...
pthread_mutex_lock(&m); pthread_mutex_lock(&m); pthread_create(&prod, &atributos, func_prod, NULL);
while (! condición) ... pthread_create(&cons, &atributos, func_cons, NULL);
pthread_cond_wait(&c, &m); // Se produce la condición pthread_join(prod,NULL);
// Resto de la S.C. pthread_cond_broadcast(&c); pthread_join(cons,NULL);
pthread_mutex_unlock(&m); pthread_mutex_unlock(&m); }
Sincronizació
Sincronización de pthreads.
pthreads. Ejemplos Sincronizació
Sincronización de pthreads.
pthreads. Ejemplos

„ Ejemplo1: Productor/Consumidor (2) „ Ejemplo1: Productor/Consumidor (3)


– Código de los hilos productor y consumidor: – Código de buffer_insertar y buffer_extraer:

void *func_prod(void *p) { void *func_cons(void *p) { void buffer_insertar(int item) { void buffer_extraer(int *item){
int item; int item; pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
S02 Sistemas operativos II

S02 Sistemas operativos II


while(1) { while(1) { while (contador == N) { while (contador == 0){
item = producir(); buffer_extraer(&item); pthread_cond_wait(&lleno, pthread_cond_wait(&vacio,
buffer_insertar(item); consumir(item); &mutex); &mutex);
} } } }
pthread_exit(0); pthread_exit(0); buffer[entrada] = item; item = buffer[salida];
} } entrada = (entrada + 1) % N; salida = (salida + 1) % N;
contador = contador + 1; contador = contador - 1;
pthread_cond_broadcast(&vacio); pthread_cond_broadcast(&lleno);

pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
} }

También podría gustarte