Está en la página 1de 6

Problema productor-consumidor

En computacin, el problema del productor-consumidor es un ejemplo clsico de problema


de sincronizacin de multiprocesos. El programa describe dos procesos, productor y
consumidor, ambos comparten un buffer de tamao finito. La tarea del productor es generar un
producto, almacenarlo y comenzar nuevamente; mientras que el consumidor toma
(simultneamente) productos uno a uno. El problema consiste en que el productor no aada
ms productos que la capacidad del buffer y que el consumidor no intente tomar un producto
si el buffer est vaco.
La idea para la solucin es la siguiente, ambos productos se ejecutan simultneamente y se
despiertan o duermen segn el estado del buffer. Concretamente, el productor agrega
productos mientras quede espacio y en el momento en que se llene el buffer se pone a
dormir. Cuando el consumidor toma un producto notifica al productor que puede comenzar a
trabajar nuevamente. En caso contrario si el buffer se vaca, el consumidor se pone a dormir y
en el momento en que el productor agrega un producto crea una seal para despertarlo. Se
puede encontrar una solucin usando mecanismos de comunicacin interprocesos,
generalmente se usan semforos. Una inadecuada implementacin del problema puede
terminar en un deadlock donde ambos procesos queden en espera de ser despertados. Este
problema pude ser generalizado para mltiples consumidores y productores.
ndice
[ocultar]

1Aproximacin errnea

2Solucin usando semforos

3Haciendo uso de monitores

4Vase tambin

Aproximacin errnea[editar]
Para resolver el problema cualquier programador podra llegar a la solucin que se muestra a
continuacin. En la misma se utilizan dos bibliotecas, sleep y wakeup . La variable
global itemCount contiene el nmero de elementos en el buffer.
int itemCount = 0;
procedure producer() {
while (true) {
item = produceItem();
if (itemCount == BUFFER_SIZE) {
sleep();
}

putItemIntoBuffer(item);
itemCount = itemCount + 1;
if (itemCount == 1) {
wakeup(consumer);
}
}
}
procedure consumer() {
while (true) {
if (itemCount == 0) {
sleep();
}
item = removeItemFromBuffer();
itemCount = itemCount - 1;
if (itemCount == BUFFER_SIZE - 1) {
wakeup(producer);
}
consumeItem(item);
}
}

El problema con esta aproximacin es que puede caer en un deadlock, considere el siguiente
escenario:
1. El consumidor acaba de consultar la variable itemCount , nota que es cero y pasa a
ejecutar el bloque if .
2. Justo antes de llamar a la funcin sleep() el consumidor es interrumpido y el
productor comienza a trabajar.
3. El productor crea un objeto, lo agrega al buffer y aumenta itemCount .
4. Como el buffer estaba vaco antes de la ltima adicin el productor intenta despertar al
consumidor.

5. Desafortunadamente el consumidor no estaba durmiendo todava luego la llamada


para despertarlo se pierde. Una vez que el consumidor comienza a trabajar
nuevamente pasa a dormir y nunca ms ser despertado. Esto pasa porque el
productor solo lo despierta si el valor de itemCount es 1.
6. El productor seguir trabajando hasta que el buffer se llene, cuando esto ocurra se
pondr a dormir tambin.
Como ambos procesos dormirn por siempre, hemos cado en un deadlock. La esencia del
problema es que se perdi una llamada enviada para despertar a un proceso que todava no
estaba dormido. Si no se perdiera, todo funcionara. Una alternativa rpida seria agregar un bit
de espera de despertar a la escena. Cuando se enva una llamada de despertar a un proceso
que est despierto, se enciende este bit. Despus, cuando el proceso trata de dormirse, si el
bit de espera de despertar est encendido, se apagar, pero el proceso seguir despierto. El
bit de espera de despertar acta como una alcanca de seales de despertar. Aunque el bit de
espera de despertar soluciona este caso, es fcil construir ejemplos con tres o ms procesos
en los que un bit de espera de despertar es insuficiente. Se podra agregar un segundo bit de
espera de despertar, o quiz 8 o 32, pero en principio el problema sigue ah.

Solucin usando semforos[editar]


El uso de semforos resuelve el problema de las llamadas perdidas. En la solucin que se
muestra a continuacin se usan dos semforos fillCount y emptyCount . El primero es el
nmero de objetos que existen en el buffer mientras que emptyCount es el nmero de
espacios disponibles donde se puede insertar objetos. fillCount es incrementado
y emptyCount es decrementado cuando se inserta un objeto en el buffer. Si el productor
intenta decrementar emptyCount cuando su valor es cero, el productor es puesto a dormir.
La prxima vez que se consuma un objeto el productor despierta. El consumidor funciona
anlogamente.
semaphore fillCount = 0; // items produced
semaphore emptyCount = BUFFER_SIZE; // remaining space
procedure producer() {
while (true) {
item = produceItem();
down(emptyCount);
putItemIntoBuffer(item);
up(fillCount);
}
}
procedure consumer() {
while (true) {
down(fillCount);
item = removeItemFromBuffer();

up(emptyCount);
consumeItem(item);
}
}

La solucin anterior funciona perfectamente cuando hay solo un productor y un consumidor.


Cuando mltiples consumidores o productores comparten el mismo espacio de memoria para
almacenar el buffer la solucin anterior pude llevar a resultados donde dos o ms procesos
lean o escriban la misma regin al mismo tiempo. Para entender como puede ser esto posible
imagine como puede ser implementada la funcin putItemIntoBuffer() . Podra contener
dos acciones, una busca un posible espacio y la otra escribe en l. Si la funcin puede
ejecutarse concurrentemente por mltiples productores entonces el siguiente escenario es
posible:
1. Dos productores decrementan emptyCount
2. Un productor determina el siguiente espacio donde insertar el objeto.
3. El segundo productor determina el siguiente espacio para insertar el objeto y resulta
ser el mismo del primer productor.
1. Ambos intentan escribir al mismo tiempo.
Para sobreponerse a este problema necesitamos asegurarnos que solo un productor este
ejecutando el mtodo putItemIntoBuffer() a la vez. En otras palabras necesitamos de
algn modo tener una seccin crtica con exclusin. La solucin para mltiples productores y
consumidores se muestra a continuacin.
semaphore mutex = 1;
semaphore fillCount = 0;
semaphore emptyCount = BUFFER_SIZE;
procedure producer() {
while (true) {
item = produceItem();
down(emptyCount);
down(mutex);
putItemIntoBuffer(item);
up(mutex);
up(fillCount);
}
}

procedure consumer() {
while (true) {
down(fillCount);
down(mutex);
item = removeItemFromBuffer();
up(mutex);
up(emptyCount);
consumeItem(item);
}
}

Note que el orden en que cada semforo es incrementado y decrementado es esencial, no


usar un orden apropiado puede llevar a un deadlock.

Haciendo uso de monitores[editar]


El siguiente pseudocdigo muestra una solucin al problema usando monitores. Como la
exclusin mutua est implcita en los monitores no es necesario un esfuerzo extra para
proteger la regin crtica. En otras palabras, esta variante funciona con cualquier cantidad de
productores y consumidores sin otra modificacin. Es relevante el hecho de que el uso de
monitores hace muchos menos probable la aparicin de conflictos que cuando se usan
semforos.
monitor ProducerConsumer {
int itemCount
condition full;
condition empty;
procedure add(item) {
while (itemCount == BUFFER_SIZE) {
wait(full);
}
putItemIntoBuffer(item);
itemCount = itemCount + 1;
if (itemCount == 1) {
notify(empty);
}
}
procedure remove() {
while (itemCount == 0) {

wait(empty);
}
item = removeItemFromBuffer();
itemCount = itemCount - 1;
if (itemCount == BUFFER_SIZE - 1) {
notify(full);
}

return item;
}
}
procedure producer() {
while (true) {
item = produceItem()
ProducerConsumer.add(item)
}
}
procedure consumer() {
while (true) {
item = ProducerConsumer.remove()
consumeItem(item)
}
}

Ntese el uso de la sentencia while cuando se comprueba si el buffer est lleno o vaco.
Con mltiples consumidores hay un race condition cuando un consumidor es notificado que un
objeto ha sido puesto en el buffer pero otro consumidor que est esperando en el monitor lo
extrae del buffer antes. Si en vez de una sentencia while se usa un if muchos objetos
pueden ser agregados a un buffer lleno o removidos de un buffer vaco.

También podría gustarte