Está en la página 1de 7

Programacin Concurrente y Paralela

2.5.2 Monitores
Los semforos, a pesar de su sencillez de uso, son el equivalente a las instrucciones goto y el manejo de apuntadores en los lenguajes de programacin imperativos: son muy susceptibles a errores. Su utilizacin exige disciplina. Por ejemplo, el siguiente error conduce inmediatamente a un deadlock: P(S) ; seccin crtica P(S); Generalmente resulta difcil distinguir entre los dos usos de los semforos (i.e. para exclusin mutua y condicin de sincronizacin) en un programa sin una revisin detallada de todo el cdigo. Los monitores pretenden ayudar a evitar los riesgos a que se presentan esos tipos de errores de programacin, proporcionando construcciones de programacin de mayor nivel de abstraccin que los semforos, ya que los monitores estn en estrecha relacin con la programacin orientada a objetos, adems de ser la primitiva para sincronizacin interconstruda que ofrece Java. Un monitor es un mdulo opaco que encapsula servicios mediante mtodos de acceso, as como sus variables locales y globales. La nica forma para manipular o acceder las variables dentro del monitor es invocando alguno de los mtodos de servicio. Solamente se permite que un hilo est activo a la vez dentro del monitor ejecutando uno de los mtodos de servicio, asegurando exclusin mutua y previniendo implcitamente la presencia de condiciones de contencin. Cada objeto monitor tiene un candado, el compilador del lenguaje de programacin genera el cdigo al comienzo de cada mtodo de servicio para adquirir el candado y al final para liberarlo. Si el monitor est ocupado por algn hilo (i.e. se apropi del candado), los hilos siguientes que invoquen alguno de los mtodos de servicio del monitor (i.e. intenten entrar al monitor) sern bloqueados e incorporados en la lista de espera para adquirir el candado. Al igual que los semforos, los monitores ofrecen las dos formas de sincronizacin: la exclusin mutua est garantizada por el compilador implcitamente al invocar mtodos de servicio. Para proporcionar mecanismos para sincronizacin de eventos (condicin de sincronizacin) un monitor puede contener variables de condicin, las cuales pueden manipularse mediante las operaciones signal y wait (que son anlogas a las operaciones P y V en semforos binarios, respectivamente). Su funcionamiento se describe enseguida: wait: Un hilo que espera a que ocurra un evento indicado por una variable de condicin deja al monitor temporalmente, libera el candado y se une a la lista de hilos bloqueados correspondiente a esa variable de condicin. signal: Cada seal con respecto a una variable de condicin despierta a un hilo de la lista de hilos bloqueados correspondiente a esa variable de condicin (no necesariamente el que lleva ms tiempo en espera), si no hay ningn hilo esperando, la seal no se almacena y no tiene efecto (contrastando a la manera en la cual los semforos funcionan). Dado que las operaciones de liberar al candado 1

Programacin Concurrente y Paralela y unirse a la lista de espera por una variable de condicin son operaciones atmicas no hay riesgo de prdida de las seales (i.e. `wakeup'). Al hilo despertado por la seal se le desplaza de esa lista de bloqueados y se le coloca en la lista de hilos en espera por entrar al monitor. Una vez que el candado sea readquirido, el hilo en cuestin contina la ejecucin del mtodo de servicio que invoc anteriormente (i.e. la primera vez para entrar al monitor). Mtodos de Servicio: metodo1(..) metodo2(..) : metodon(..) Datos Globales Variables de Condicin: Cond 1 Cond 2 : Condm

Hilos esperando entrar al monitor (invocaron a un mtodo de servicio)

lista de bloqueados por Cond 1 lista de bloqueados por Cond 2 : lista de bloqueados por Condm

Figura 2.2: Estructura de los Monitores. Las variables de condicin de los monitores no tienen valor, se le puede considerar como el nombre de la lista de hilos bloqueados (nombre de un evento). Note que las variables de condicin y los semforos difieren en dos maneras: una seal realizada en una variable cuya lista de hilos bloqueados est vaca no tiene efecto mientras que la invocacin a V incrementa el contador del semforo; a su vez, una invocacin a la primitiva wait en una variable de condicin siempre bloquea al hilo hasta que reciba una seal mientras que una invocacin a P decrementa el contador del semforo si su valor es positivo y no bloquea al hilo. El manejo de las variables de condicin de los monitores se implanta de acuerdo a una de las diferentes disciplinas de sealizacin: 1. signal and exit: si un hilo que est ejecutndose dentro del monitor emite una seal respecto una variable de condicin entonces debe dejar el monitor inmediatamente ejecutando una instruccin return en el mtodo de servicio que invoc. Se despierta a un hilo de la lista de hilos bloqueados correspondiente a la variable de condicin y contina ejecutndose dentro del monitor. 2. signal and wait: el hilo que recibe la seal (sealado o despertado) se ejecuta dentro del monitor, mientras que el hilo que emiti la seal (sealador) espera a que el sealado salga del monitor y entonces pueda continuar el sealador. 3. signal and continue: el hilo despertado espera a que el sealador deje el monitor y entonces contina su ejecucin dentro del monitor. En la Figura 2.3 se ilustra el funcionamiento de tales disciplinas (suponemos que el tiempo hacia abajo). Los monitores en Java utilizan la disciplina signal and continue, pero revisaremos primero a la ms sencilla que es signal and exit. Los mtodos de servicio se indican con la palabra reservada synchronized indicando que un slo hilo se le permite 2

Programacin Concurrente y Paralela ejecutar el mtodo a la vez, el mtodo wait(condVar) permite indicar la espera de una variable de condicin, por su parte notify(condVar) indica la emisin de una seal.

Figura 2.3: Disciplinas de sealamiento en los Monitores. Para la disciplina signal and exit, se asume lo siguiente: 1. Despus de emitir una seal respecto una variable de condicin, el sealador debe salir inmediatamente del monitor, de tal forma que ninguna variable cambia antes de que el hilo despertado contine ejecutndose dentro del monitor. Por tanto, el hilo despertado encuentra que la condicin de la seal es verdadera. 2. Al hilo despertado se le otorga prioridad para proceder inmediatamente al monitor sobre aquellos hilos que estaban esperando entrar al monitor mediante la invocacin de un mtodo de servicio. Como ejemplo, mostramos (en pseudo cdigo) un fragmento para la solucin al problema del productor-consumidor utilizando un buffer limitado (archivo Monitor/bbse.java): public synchronized void deposit(double data) { if (count == size) wait(notFull); buf[rear] = data; 3

Programacin Concurrente y Paralela rear = (rear+1) % size; count++; if (count == 1) notify(notEmpty); } public synchronized double fetch() { double result; if (count == 0) wait(notEmpty); result = buf[front]; front = (front+1) % size; count--; if (count == size-1) notify(notFull); return result; } Si el buffer est lleno, el productor se bloquea con la invocacin al mtodo wait respecto a la variable de condicin notFull, el productor es despertado por el consumidor mediante la seal notify cuando deja un espacio libre en el buffer. Si el buffer est vaco, el consumidor se bloquea respecto la variable notEmpty y a su vez ser despertado por el productor cuando ste coloque un elemento en el buffer. Para la disciplina signal and continue, no se requiere que el hilo que emiti la seal salga del monitor, ni tampoco que el hilo despertado tenga prioridad para proceder dentro del monitor sobre los dems hilos que estn esperando entrar (compiten por el candado). Por ello, no se puede garantizar que la condicin que condujo la emisin de la seal continue siendo vlida cuando el hilo que fue despertado entre de nuevo al monitor, ya que antes de dejar al monitor el hilo que emiti la seal podr haber cambiado datos y/o alterado el estado interno del monitor. A su vez, los hilos que compiten por entrar al monitor pueden adelantrseles a los hilos que estn esperando a que ocurra el evento de la variable de condicin, lo cual resulta en una forma de inanicin (pues el tiempo de espera asociado a la variable de condicin no est limitado). Debido a ello, se deben tomar precauciones para que el hilo que estaba esperando por la variable de condicin verifique que sta haya ocurrido una vez que entra de nuevo al monitor, i.e. fundamentalmente se debe cambiar if (condicion) wait(); // bloqueo en signal-and-exit por un bucle: while (condicion) wait(); // bloqueo en signal-and-continue incluso permitiendo que la seal pueda despertar a ms de un hilo bloqueado (i.e una forma de broadcast). Para construir un monitor en Java, se debe utilizar el modificador synchronized en cada mtodo de servicio (i.e que requiera exclusin mutua), tales mtodos generalmente son pblicos pero pueden tambin ser privados. Cada objeto Java tiene un candado asociado; implcitamente, un hilo que quiera ejecutar un mtodo synchronized de un objeto primero debe apropiarse del candado, bloquendose si es que est en uso por otro hilo. Desafortunadamente, cada monitor en Java slo tiene una variable de condicin 4

Programacin Concurrente y Paralela annima; todas las invocaciones a wait() y notify() (para bloquear y emitir una seal, respectivamente) se refieren automticamente a esa variable annima. Por su parte, la primitiva notifyAll() permite despertar a todos los hilos que se encuentran bloqueados esperando por la variable de condicin annima. Como ejemplos, se muestran segmentos de cdigo para los problemas productorconsumidor con buffer limitado (prog. Monitor/bbmo.java) y los filsofos comelones (Monitor/dpmo.java): public synchronized void deposit(double value) { while (count == numSlots) try { wait(); } catch (InterruptedException e) { System.err.println("interrupted out of wait"); } buffer[putIn] = value; putIn = (putIn + 1) % numSlots; count++; // wake up the consumer if (count == 1) notify(); // since it might be waiting } public synchronized double fetch() { double value; while (count == 0) try { wait(); } catch (InterruptedException e) { System.err.println("interrupted out of wait"); } value = buffer[takeOut]; takeOut = (takeOut + 1) % numSlots; count--; // wake up the producer if (count == numSlots-1) notify(); // since it might be waiting return value; } Ntese que no ocurren condiciones de contencin sobre la variable compartida count. private void seeIfStarving(int k) { if (state[k] == HUNGRY && state[left(k)] != STARVING && state[right(k)] != STARVING) { state[k] = STARVING; System.out.println("philosopher " + k + " is STARVING"); } } private void test(int k, boolean checkStarving) { 5

Programacin Concurrente y Paralela if (state[left(k)] != EATING && state[left(k)] != STARVING && (state[k] == HUNGRY || state[k] == STARVING) && state[right(k)] != STARVING && state[right(k)] != EATING) state[k] = EATING; else if (checkStarving) seeIfStarving(k); // simplistic naive check for starvation } public synchronized void takeForks(int i) { state[i] = HUNGRY; test(i, false); while (state[i] != EATING) try {wait();} catch (InterruptedException e) {} } public synchronized void putForks(int i) { state[i] = THINKING; test(left(i), checkStarving); test(right(i), checkStarving); notifyAll(); } En este programa se utiliza un esquema simple para prevencin de inanicin. Dado que Java solamente soporta una variable de condicin nica en sus monitores no se puede utilizar un arreglo de variables de condicin (una para cada filsofo) para bloquear a un filosofo hambriento que no puede comer. En lugar de ello, se utiliza notifyAll() para despertar a todos los filsofos que estn esperando a que su vecino deje de comer; lo cual es muy ineficiente pues a lo ms dos filsofos necesitan ser despertados. Consecuentemente, la mayora de los mtodos de monitores en Java siguen el siguiente patrn de diseo: public synchronized tipo metodo(...) { ... notifyAll(); // si alguna condicin de espera fu alterada while(!condicion) try { wait(); }catch (InterruptedException e) {} ... notifyAll(); // si alguna condicin de espera fu alterada } A pesar de que los monitores en Java solamente tienen una variable de condicin annima, se puede utilizar un objeto para implantar una variable de condicin con nombre, que actuar como un objeto para notificacin: objeto compartido: Object obj = new Object(); 6

Programacin Concurrente y Paralela en un hilo: synchronized(obj) { if (!cond) try { obj.wait();} catch(InterruptedException e){} ... } en el otro: synchronized(obj) { if (cond) obj.notify(); ... }

Dentro de un bloque de exclusin mutua en obj un hilo verifica la condicin para ver si puede continuar, de lo contrario espera. El otro hilo cambia la condicin y notifica al hilo en espera. Cuando se usa dentro de un monitor, un objeto de notificacin juega el rol de una variable de condicin con nombre. Cabe mencionar que las invocaciones anidadas a mtodos de servicio de los monitores estn sujetos a deadlocks: class S { synchronized void f(T t) { ... t.g(...); ... }} class T { synchronized void g(S s) { ... s.f(...); ... }}

Aqu, s y t son referencias a monitores creados en las clases S y T respectivamente, y que son compartidos por dos hilos A y B en la siguiente secuencia de eventos: A: invoca s.f(t); B: invoca t.g(s); A: se bloquea al invocar t.g B: se bloquea al invocar s.f Para evitar deadlocks, se sugiere ordenar globalmente todos los objetos monitor y solicitar que todos los hilos que compiten por el candado del monitor sigan el mismo orden.

También podría gustarte