En este post estudiaremos 4 problemas clásicos de sincronización por software, los cuales son:
- Problema de lectores y escritores
- Problema de productor consumidor
- Problema de los filósofos comensales
- Problema del babero dormilón
Cabe decir que estos problemas fueron diseñados para poder abstraer problemas de la vida real, y si los tomamos literalmente puede que no le encontremos ningún uso practico pero, como todo en la vida, es cuestión de interpretación.
Consideremos que para todas las soluciones usaremos semáforos cuyos métodos (en pseudocodigo) son:
//Inicializar el semaforo "s" a un valor "m"
IS(s, m) {
S = m;
}
//down, también conocido como wait
//el metodo bloquear procesos, impide el acceso a la región critica de memoria.
DOWN(s) {
if(s > 0) {
s = s - 1
} else {
bloquear_procesos();
}
}
//UP tambien conocido como notify o signal vuelve a liberar la región critica
UP(s) {
if(hay_procesos_dormidos()){
despertar_un_proceso();
} else {
s = s + 1;
}
}
Problema de lectores y escritores
Consiste de dos procesos que acceden el mismo bloque de memoria compartida al mismo tiempo tomando en cuenta la restricción de que dos acciones no pueden ser llevadas a cabo sobre el mismo bloque de memoria al mismo tiempo, es decir, si un proceso esta escribiendo un bloque, este no puede ser escrito ni leído por ningún otro proceso manteniendo así la exclusión mutua sobre una región critica.
Solución:
semaphore mutex = 1; // controla el acceso al contador de procesos leidos
semaphore rc = 1; // controla el acceso a la región critica
int reader_count; // el numero de procesos lectores que acceden la región critica
Lector()
{
while (true) { // loop infinito
down(&mutex); // acceder reader_count
reader_count = reader_count + 1; // incrementar reader_count
if (reader_count == 1)
down(&rc); // si es el primer proceso en leer la reión critica,
// se bloquea la misma con un down para prevenir
// que esta sea accesada por otro proceso
up(&mutex); // permitir a otros procesos acceder a reader_count
leer_rc(); // leer la region critica
down(&mutex); // acceder reader_count
reader_count = reader_count - 1; // decrementar reader_count
if (reader_count == 0)
up(&rc); // si ya no hay procesos leyendo
// la region critica, permitir que el proceso escritor funcione
up(&mutex); // permitir a otros procesos acceder el lector
}
Escritor()
{
while (TRUE) { // loop infinito
crear_datos(); // preparar datos para escribir en la rc
down(&rc); // acceder a la región critica
write_db(); // escribir datos
up(&rc); // liberar el acceso exclusivo a la rc
}
Problema de productores consumidores
Este problema describe dos procesos que acceden a un buffer de tamaño fijo al mismo tiempo y lo utilizan como una cola.
El proceso productor produce, valga la redundancia, un recurso o dato y lo almacena en la cola, mientras que por su parte el proceso consumidor consume los datos que el productor va produciendo, siempre que existan datos o recursos que consumir.
semaphore lleno = 0; // items produced
semaphore vacio = BUFFER_SIZE; // remaining space
proceso producer() {
while (true) {
item = producir(); //produce un item
down(vacio); //reduce la cuenta de vacio, si esta lleno espera
push(item); //coloca el item en la cola
up(lleno); //aumenta el semaforo de cola llena
}
}
proceso consumidor() {
while (true) {
down(lleno); //quita un elemento del semaforo lleno (permite que se vuelva a producir)
item = pop(); //obtenemos el elemento
up(vacio); //aumenta uno a la cuenta de vacio
consumir(item); //consume el elemento
}
}
Problema de los filósofos comensales
El enunciado dice así:
5 filósofos comensales se sientan en una mesa redonda con un plato de espaguetti en el centro, y un tenedor es colocado en medio de cada par de filósofos. Cada filosofo pasa su vida pensando y comiendo, pero necesita dos tenedores para poder comer. Cada tenedor puede ser usado por un solo filosofo a la vez y cuando un filosofo termina de comer debe poner los tenedores de vuelta en su lugar.
Siendo que el espaguetti es infinito y que cada filosofo no sabe cuando los otros quieren comer o pensar, el problema reside en diseñar un algoritmo concurrente tal que ninguno de los filósofos morirá de hambre.
Para esta solución consideremos las siguientes variables:
- pensar (p)
- comer (c)
- hambriento (h)
- dormir (d)
y un arreglo de 5 semáforos, y un semáforo de exclusión mutua.
Semaphore Em //semaforo de exclusion mutua
Semaphore[] sems = new Semaphore[5]; //arreglo de semaforos
Status = ['p', 'p', 'p', 'p', 'p']; //que esta haciendo cada filosofo en determinado momento
is(em, 1); //inicializar exclusión mutua
//inicializar el arreglo de semaforos (uno por filosofo)
for(i=0; i<sems.length; i++) {
is(sems[i]);
}
//proceso del i-esimo filosofo
filosofo(i) {
pensar();
tomar_tenedores(i);
comer();
dejar_tenedores(i);
dormir(i);
}
//tomar tenedor del i-esimo filosofo
tomar_tenedores(i){
down(em); //bloquear los recursos de rc
status[i] = 'h'; //indicar que esta hambriento
test(i); //intentar comer
up(em); //bloquear los recursos de rc
down(sems[i]); //bajar el semaforo de este filosofo
}
//intentar comer para el i-esimo filosofo
test(i) {
if(
Status[i+1 % 5] != 'c' && // el filosofo de la derecha no esta comiendo
Status[i-1 % 5] != 'c' && // el filosofo de la derecha no esta comiendo
Status[i] = 'h' //este filosofo tiene hambre
) {
Status[i] = 'c'; //como hay tenedores podemos comer
up(sems[i]); //bloqueamos la región critica
}
}
dejar_tenedores(i) {
down(em);
Status[i] = 'd';
test( (i-1) % 5 ); //dejar tenedor izquierdo
test( (i+1) % 5 ); //dejar teedor derecho
}
Problema del barbero dormilón
El problema consiste en un barbero que debe atender a sus clientes como van llegando, cuando un cliente llega se sienta en la sala de espera y notifica al barbero, que en dado caso no tenga clientes esta dormido, el barbero despierta y corta el pelo, cuando termina de cortar el pelo revisa si tiene mas clientes para seguir cortando el pelo, o bien seguir durmiendo.
Semaphore barberoListo = 0
Semaphore accessarSillas = 1 # si es 1 los asientos en la sala de sepera pueden aumentar a disminuir
Semaphore clienteList = 0 # cantidad de clientes esperando ser atendidos
int asientosLibres = N # numero de asientos en la sala de espera
def Barbero():
while true: # loop infinito
down(clienteList) # tratar de atendender un cliente, si no hay clientes dormir
down(accessWRSeats) # intentar accesar para modificar el numero de asientos libres, si no dormir
asientosLibres += 1 # Una silla en la sala de espera esta libre
up(barberoListo) # el barbero esta listo para cortar
up(accessarSillas) # desbloquear la silla
# (cortar_pelo)
def Cliente():
while true: # loop infinito
down(accessWRSeats) # tratar de acceder a las sillas en la sala de espera
if asientosLibres > 0: # si hay sillas libres:
asientosLibres -= 1 # sentarse en la silla
up(clienteList) # despertar al barbero dormilon
up(accessarSillas) # desbloquear la silla
down(barberoListo) # esperar a que el barbero este listo
# (cortarse el pelo)
else: # no hay asientos libres en la sala de espera
up(accessarSillas) # liberar el bloqueo en las sillas
# (abandonar sin cortarse el pelo)