Vous êtes sur la page 1sur 149

LPRS

2013
Teora
Monitores
Prcticas

TEORA

INTERBLOQUEOS
Pregunta 1 (2 puntos) sept 2007
Dos hebras (threads) de Java, A y B, invocan respectivamente los meetodos opA y opB de un
mismo objeto de una clase Gestor, cuya definicioon (simplificada) es:
public class Gestor {
private Recurso R = new
Recurso(); private Recurso S =
new Recurso();
public void opA () {
R.adquiere();
S.adquiere();
// otras operaciones
S.libera();
R.libera();
}
public void opB () {
S.adquiere();
R.adquiere();
// otras operaciones
R.libera();
S.libera();
}
}

La clase Recurso se define de forma simplificada como sigue:


public class Recurso {
private boolean ocupado = false;
public syncronized void adquiere ()
{
while (ocupado) wait();
ocupado = true;
}
public synchronized void libera () {
ocupado = false;
notifyAll();
}
}

Indicar si el codigo anterior presenta algun problema de vivacidad (interbloqueos, bloqueos vivos,
inanicion). Justificar la respuesta y, en caso afirmativo, indicar que habra que hacer para corregirlo.
NOTA: Se ha omitido el codigo relacionado con el tratamiento de excepciones. No debe tenerse en
cuenta esta omision.
El meetodo adquiere de la clase Recurso bloquea la hebra que lo invoca mientras el recurso estee
ocupado, y cuando estaa libre lo marca como ocupado y continuua. Por tanto, asigna el recurso
de forma exclusiva a una hebra. por otra parte, el meetodo libera marca el recurso como libre
y avisa a todas las hebras que pudieran estar esperando adquirirlo. Noo tese que no hay
posibilidad de quitar un recurso a una hebra que lo ha adquirido.
Los meetodos opA y opB de la clase Gestor no estaa n sincronizados, por lo que se pueden
ejecutar concurrentemente. En cada uno de ellos la hebra que llama adquiere de forma exclusiva los recurso R y S, en distinto orden. Noo tese que una vez adquirido un recurso la hebra se
puede quedar bloqueada esperando adquirir el otro. Por otra parte, se puede dar una situa- cioo

n de espera circular, si A adquiere R y B adquiere S concurrentemente, y ambas hebras se


quedan bloqueadas esperando adquirir S y R, respectivamente.
Se dan, pues, las condiciones necesarias para que pueda haber interbloqueos: exclusioon mu- tua
en el acceso a recurso, no expulsioon, retener y esperar, y espera circular.
Para evitar este problema se debera escribir el coo digo de forma que los recurso se adquieran
siempre en el mismo orden (por ejemplo, primero R y despuees S). De esta forma se evita la
espera circular.
Tambieen se evitara el interbloqueo haciendo sincronizados los meetodos opA y opB. As se
evitara la condicioo n tener y esperar. El inconveniente de esta solucioo n es que se reducira la
concurrencia, ya que las otras operaciones de opA y opB no se podran ejecutar concurrentemente.

Pregunta 1 (1 punto) Jun 2008


Un programa concurrente consta de tres hebras, A, B y C, que usan de forma mutuamente
exclusiva tres recursos compartidos, X, Y y Z, con arreglo al siguiente esquema, que se repite
indefinidamente para cada hebra:
A: adquiere X; usa X; libera X; otras operaciones;
B: adquiere Y; adquiere Z; usa Y,Z; libera Z; libera Y; otras
operaciones; C: adquiere Z; adquiere X; usa X,Z; libera X; libera Z;
otras operaciones;
Analizar razonadamente si es posible que se produzcan interbloqueos (deadlocks) entre las hebras A,
B y C, a partir de las condiciones necesarias para ello.
Las condiciones necesarias para que se produzca un interbloqueo son:
1. Exclusioo n mutua en el uso de recursos.
2. Retener recursos mientras se espera el acceso a otros.
3. No se puede quitar un recurso a un proceso que lo tiene asignado.
4. Espera circular.
La condicioo n 1 se da por definicioo n.
La condicioo n 2 se da u nicamente en las hebras B y C, ya que A soo lo usa un recurso. Por tanto, A
no puede participar en ninguu n interbloqueo.
La condicioo n 3 estaa implcita en los esquemas anteriores.
En cuanto a la condicioo n 4, la u nica posibilidad de espera simultaa nea de B y C ocurre si B ha
adquirido Y y estaa esperando adquirir Z, y al mismo tiempo C ha adquirido Z y estaa esperando
adquirir X. Si C estaa esperando, la u nica posibilidad es que A estee usando X, por lo que no
depende de B. Por tanto, no hay espera circular y no puede haber interbloqueos.
Y -> B -> Z -> C -> X -> A

Pregunta 3 (1 puntos) Sept 2008


En un programa concurrente hay tres hebras, T1, T2 y T3, que acceden a dos recursos compartidos,
R1 y R2, de la siguiente forma:
T1: ..; adquiere R1; adquiere R2; usa R1; usa R2; libera R2; libera R1; ...
T2: ..; adquiere R2; usa R2; libera R2; adquiere R1; usa R1; libera R1; ...
Analizar razonadamente si es posible que se produzcan interbloqueos (deadlocks) entre las hebras
T1 y T2, teniendo en cuenta las condiciones necesarias para un interbloqueo.
Las condiciones necesarias para que se produzca un interbloqueo son:
1. Exclusioo n mutua en el uso de recursos.
2. Retener recursos mientras se espera el acceso a otros.
3. No se puede quitar un recurso a un proceso que lo tiene asignado.
4. Espera circular.

La condicion 1 se da por definicion.


La condicion 2 se da unicamente en la hebra T1, ya que T2 libera R2 antes de adquirir R1.
Por tanto, T2 no puede participar en ningun interbloqueo.
La condicion 3 esta implcita en los esquemas anteriores.
En cuanto a la condicion 4, la unica posibilidad de espera simultanea de T1 y T2 ocurre si T1 ha
adquirido R1 y esta esperando adquirir R2, y al mismo tiempo T2 ha adquirido R2. Pero como
T2 no espera adquirir ningun recurso mas antes de usar y liberar R2, no hay espera circular.
Por tanto, las condiciones 2 y 4 no se dan, y no puede haber interbloqueos.

Pregunta 1 (1 punto) Jun 2009


Sea la siguiente clase de Java:
public class Data {
private int value = 0;
public Data() {value = 0;}
public synchronized int get () {return value;}
public synchronized void set (int x) {value = x;}
public void reset () {value = 0;}
}

En un programa se ejecutan dos hebras concurrentes, Q y R, que son objetos de la siguiente clase:
public class Client extends Thread {
private Data data;
public Client (Data x) {data = x}
public void run() { int
x; while(true) {
x = data.get(); x =
x+1;
if (x < 10) {
data.set(x);
} else {
data.reset();
}
}
}
}

Suponemos que en el programa principal se crean los siguientes objetos:


Data X = new Data();
Client Q = new Client(X);
Client R = new Client(X);

Est garantizada la exclusin mutua entre Q y R cuando acceden al objeto X? Justificar la respuesta.
La exclusion mutua entre Q y R est garantizada nicamente cuando ejecutan los mtodos
get y set, ya que se trata de mtodos sincronizados. Sin embargo, cuando alguna de las
dos hebras invoca reset se pueden producir condiciones de carrera si la otra hebra invoca
concurrentemente cualquiera de los mtodos de X, ya que reset no est sincronizado.

Pregunta 2 (1 punto) Sept 2009


En un programa concurrente hay tres hebras, A, B y C, que acceden a tres recursos compartidos, X,
Y y Z, de la siguiente forma:
A: ...; adquiere X; adquiere Y; usa X; usa Y; libera Y; libera X; ...
B: ...; adquiere X; adquiere Z; usa X; usa Z; libera Z;
libera X; ... C: ...; adquiere Y; adquiere Z; usa Y; usa Z;
libera Z; libera Y; ...
Analizar razonadamente si es posible que se produzcan interbloqueos (deadlocks) entre las hebras
anteriores,
teniendo en cuenta las condiciones necesarias para un interbloqueo.

Se supone que las operaciones adquiere y libera son equivalentes a las operaciones lock y unlock de
un cerrojo (mutex) asociado al respectivo recurso.
Las condiciones necesarias para que se produzca un interbloqueo son:
1. Exclusin mutua en el uso de recursos.
2. Retener recursos mientras se espera el acceso a otros.
3. No se puede quitar un recurso a un proceso que lo tiene asignado.
4. Espera circular.
La condicin 1 se da por definicin.
La condicin 2 se da en las tres hebras.
La condicin 3 est implcita en los esquemas
anteriores.
En cuanto a la condicin 4, puede ocurrir que A haya adquirido X y C haya adquirido Y, y
estn esperando respectivamente acceder a Y y Z. Como Z est libre (pues en caso contrario B
tendra a X y A no lo podra haber adquirido), C puede continuar y acabar liberando Y, con lo
que A podr continuar tambin.
Otra posibilidad es que B haya adquirido X y C haya adquirido Y, y ambas intenten adquirir Z.
Una de las dos obtendr este recurso, y lo liberar en algn momento, por lo que la otra hebra
podr continuar tambin.
Por tanto, no puede haber espera circular, y no puede haber interbloqueos.

Pregunta 1 (1 punto) Sept 2010


En un programa concurrente hay dos hebras, P y Q, que acceden a tres recursos compartidos, RX, RY
y RZ, a los que se accede de forma mutuamente exclusiva mediante sendos cerrojos (mutex), denominados
respectivamente X, Y y Z. Las hebras ejecutan las siguientes operaciones:
P: ...
X.lock();
5. lock(); ... Y.unlock();
6. lock(); ...
Z.unlock();
X.unlock();
...
Q: ...
Z.lock();
6
lock(); ...
Y.unlock();
Z.unlock();
...

Analizar razonadamente si es posible que se produzcan interbloqueos (deadlocks) entre las hebras
anteriores, teniendo en cuenta las condiciones necesarias para un interbloqueo.
Las condiciones necesarias para que se produzca un interbloqueo son:
Exclusin mutua en el uso de recursos.
Retener recursos mientras se espera el acceso a otros.
No se puede quitar un recurso a un proceso que lo tiene asignado.
Espera circular.
La condicin 1 se da por definicin.
La condicin 2 se da en las tres hebras.
La condicin 3 est implcita en los esquemas anteriores.
En cuanto a la condicin 4, puede ocurrir que P haya adquirido RX y est esperando adquirir RZ
(en Z.lock()) al mismo tiempo que Q haya adquirido RZ. En este caso no hay espera circular,
como tampoco la hay si Q invoca Y.lock(), ya que P no puede estar esperando acceder a RY y
RZ a la vez (ver figura). No hay ninguna otra posibilidad de espera circular, y por tanto se puede
afirmar que no puede haber interbloqueos.

Pregunta 2 (1 punto) Sept 2011


En un programa concurrente hay dos hebras S y T que usan tres objetos compartidos, X, Y y Z, a
los que se accede de forma mutuamente exclusiva mediante mtodos sincronizados definidos en las
clases correspondientes, que se denominan genricamente read() y write(). Las hebras ejecutan las
siguientes operaciones:
// thread S :
...
write (X.read() + Y.read() + Z.read());
write (Y.read() - Z.read());
...
// thread T :
...
Z.write(X.read() + Y.read());
Y.write(Z.read());
...

Analizar razonadamente si es posible que se produzcan interbloqueos (deadlocks) entre las hebras
anteriores.
Los mtodos read() y write() de cada objeto se ejecutan de forma mutuamente exclusiva. Por tanto,
mientras una hebra ejecuta uno de estos mtodos la otra hebra no puede ejecutar ningn mtodo del
mismo objeto. Se puede decir que una hebra que est ejecutando uno de los mtodos posee el acceso
exclusivo al objeto correspondiente mientras se ejecuta el mtodo. La exclusin mutua es la primera
condicin para que puedan producirse interbloqueos, y como hemos visto se cumple en este programa.
Por el contrario, si suponemos, como parece natural, que los mtodos read() y write() de un objeto no
llaman a ningn mtodo de otro objeto, no se cumple la segunda condicin, tener y esperar, ya que
mientras una hebra tiene el acceso exclusivo a un objeto (mientras est ejecutando read() o write() en
ese objeto) no intenta acceder a otro objeto (no invoca ningn otro mtodo).
No es necesario analizar ms condiciones. La conclusin es que en el programa mostrado no puede
haber interbloqueos.

Pregunta 1 (1 punto) Jun 2012


En un programa concurrente hay dos hebras, S y T, que acceden a tres recursos compartidos, P, Q y R, a
los que se accede de forma mutuamente exclusiva mediante sendos cerrojos, denominados
respectivamente P.mutex, Q.mutex y R.mutex. Las hebras ejecutan las siguientes operaciones:
S: ...
P.mutex.lock();
R.mutex.lock(); ... R.mutex.unlock();
P.unlock();
...
T: ...
R.mutex.lock();
Q.mutex.lock(); ... Q.mutex.unlock();
mutex.lock(); ... P.mutex.unlock();
R.mutex.unlock();
...

Analizar razonadamente si es posible que se produzcan interbloqueos (deadlocks) entre las hebras
anteriores, teniendo en cuenta las condiciones necesarias para un interbloqueo.
Las condiciones necesarias para que se produzca un interbloqueo son:
Exclusin mutua en el uso de recursos.
Retener recursos mientras se espera el acceso a otros.
No se puede quitar un recurso a un proceso que lo tiene asignado.
Espera circular.

La condicin 1 se da por el uso de los cerrojos en el acceso a los recursos.


La condicin 2 se da en las dos hebras: S bloquea Q y espera P, T bloquea R y espera Q y
luego P. La condicin 3 est implcita en el uso de los cerrojos que se muestra en el cdigo
del enunciado. En cuanto a la condicin 4, puede ocurrir que S haya adquirido P y est
esperando adquirir R, y que
al mismo tiempo al mismo tiempo T haya adquirido R y est esperando adquirir P. En este
caso hay una espera circular (ver figura), por lo que se puede afirmar que puede haber
interbloqueos.

Pregunta 1 (1 punto) Jun 2013


En un programa concurrente hay tres hebras, A, B y C, que utilizan dos objetos compartidos, P y Q,
a los que se accede de forma mutuamente exclusiva mediante sendos cerrojos, denominados
respectivamente P.mutex y Q.mutex. Las hebras ejecutan las siguientes operaciones:
A: ...
P.mutex.lock();
Q.mutex.lock(); ... Q.mutex.unlock();
P.mutex.unlock();
...
B: ...
P.mutex.lock();
...
P.mutex.unlock();
Q.mutex.lock();
...
Q.mutex.unlock();
...
C: ...
Q.mutex.lock();
mutex.lock(); ... P.mutex.unlock();
Q.mutex.unlock();
...
Q.mutex.lock();
...
Q.mutex.unlock();
...

Analizar razonadamente si es posible que se produzcan interbloqueos (deadlocks) entre todas o


algunas de las hebras anteriores, examinando las condiciones necesarias para un interbloqueo.
Las condiciones necesarias para que se produzca un interbloqueo son:
Exclusin mutua en el uso de recursos.
Retener recursos mientras se espera el acceso a otros.
No se puede quitar un recurso a un proceso que lo tiene asignado.
Espera circular.
La condicin 1 se da por el uso de los cerrojos en el acceso a los
recursos.
La condicin 2 se da en las hebras A y C: R bloquea P y espera Q, C bloquea Q y espera P. La
hebra B no cumple esta condicin, por lo que no puede participar en un interbloqueo.
Obsrvese que C tambin bloquea Q (al final del cdigo) sin esperar P. Por tanto, C no puede
participar en un interbloqueo en el segundo acceso a Q, slo en el primero.
La condicin 3 est implcita en el uso de los cerrojos que se muestra en el cdigo del
enunciado.
En cuanto a la condicin 4, puede ocurrir que A haya adquirido P y est esperando adquirir Q,
y que al mismo tiempoC haya adquirido Q y est esperando adquirir P. En este caso hay una
espera circular
(ver figura), por lo que puede haber interbloqueos.

TEORIA
Pregunta 1 (1 punto) Sept 2005
En un sistema con varios procesos concurrentes, se puede producir un cambio de contexto en
cualquier instante o soolo en determinados momentos? Justificar y explicar brevemente la respuesta.
Los cambios de contexto se pueden producir u nicamente cuando ocurre una de estas dos
cosas:
1. Se recibe una interrupcioon cuyo tratamiento obliga a cambiar el proceso en ejecu- cioon
(por ejemplo, una interrupcioon de reloj que marca el fin de la rodaja de tiempo asignada
al proceso en ejecucioon).
2. El proceso en ejecucioon efectuua una llamada al sistema que produce un cambio del
proceso en ejecucioon (por ejemplo, si se suspende en un semaaforo o en una llamada
a una operacioo n protegida).

Pregunta 2 (1,5 puntos) Sept 2007


Explique brevemente quee es la reentrancia y si tiene alguun coste en eficiencia.
Un procedimiento o meetodo es reentrante si puede ser interrumpido y llamado nuevamente
(por otra hebra o interrupcioo n) sin afectar al resultado. Requiere que las variables locales
esteen en la pila y que se acceda a los recursos compartidos en exclusioon mutua. Ambas cosas
pueden representar un coste en tiempo de ejecucioo n, generalmente pequenn o.

Pregunta 3 (1,5 puntos) Sept 2007


Explique la llamada al sistema de Linux que se utiliza para la redireccioon de descriptores (file descriptors), explicando brevemente coomo se usa.
La maas utilizada es
int dup2(int oldfd, int newfd);
que hace que newfd apunte al mismo flujo que oldfd, pudiee ndose usar ambos indistintamente, aunque generalmente se cerraraa el viejo y se usaraa el nuevo. Si el nuevo estuviera
abierto, se cierra. De este modo se puede redirigir cualquier flujo a descriptores muy utilizados
(0, 1, 2), especialmente por los filtros.

Pregunta 2 (1 punto) Sept 2008


Quee es una redireccioon de entrada/salida en unix y con quee primitivas se realiza?
Redireccioo n de entrada/salida es cambiarle a un proceso los flujos de entrada y/o salida asociados a un descriptor con el que estaa trabajando. Se utiliza normalmente antes de una operacio
o n tipo exec, para ejecutar un filtro (programa que usa los descritpores estaa ndar (0, 1, 2,
que corresponden con la entrada, salida y salida de error estaandares), sin que el filtro se entere
de con quee flujos estaa trabajando.
Para redirigir, lo que se hace es que el descriptor deseado (0, 1, 2, etc) apunte al flujo al que
queremos redirigir, que normalmente tendraa un descriptor mayor. Para ello se utilizan las
primitivas
int dup2(int oldfd, int newfd);
int dup(int oldfd);

Dup2 es la maa s usada, que hace que newfd (normalmente 0, 1 o 2) apunte al mismo flujo que
oldfd (aqueel donde queremos redirigir). El newfd previo se cierra si estuviera abierto. Con dup
se obtiene como duplicado el descriptor maa s pequenn o disponible y hay que saber previa- mente
cua
a l es. Normalmente hay que cerrar oldfd, ya que es innecesario y puede ocasionar
problemas mantenerlo abierto.

Pregunta 3 (1 punto) Jun 2008


Explicar las diferencias fundamentales entre un proceso pesado y uno ligero o hebra.

Los procesos ligeros son maa s as raa pidos de crear y es maa s raa pido el cambio de contexto.
Para ello el sistema operativo mantiene menos datos asociados a los procesos ligeros que a los
pesados. Los procesos pesados tienen un espacio de virtual de direcciones independiente,
mientras que los ligeros comparten memoria, lo que los hace vulnerables a errores de
programacioon. Los procesos pesados son ademaas unidades de proteccioon, no soolo por el espacio
de direcciones independiente, sino porque se ejecutan en nombre de un usuario con determinados
permisos sobre procesos de otros usuarios, ficheros, etc.

Pregunta 1 (1 punto) Sept 2009


Explicar por qu en un multiprocesador con memoria compartida, las posibilidades de entralazado
(interlea- ving) de instrucciones mquina, y por ello las posibilidades de carreras (race conditions),
son mayores que en un monoprocesador multiplexado.
En un monoprocesador, las multiplexaciones van guiadas por interupciones, las cules solo se
ob- servan al final del ciclo de ejecucin de una instruccin mquina. Con lo que la mnima
granulari- dad"del entrelazado en el acceso a datos compartidos ha de ser como mucho la
instruccin mquina.
En un multiprocesador con memoria compartida, sin embargo, el entrelazado en el acceso a
datos comunes va guiado por los accesos al bus comn, de los que puede haber ms de uno en
una sola instruccin mquina. Lo cual da lugar a (muchas) ms combinaciones de mezcla de
accesos irres- tringidos a datos comunes por parte de flujos concurrentes, y con ello mayor
probabilidad de que se
presenten entrelazados errneos, es decir, entrelazados que den lugar a carreras".

Pregunta 1 (1 punto) Sept 2008


Se pide explicar la razoon por la cuaal los mecanismos claasicos de sincronizacioon tales como semaaforos,
regioon crtica, etc., no se adecuan bien a la programacioon de sistemas distribuidos.
Los mecanismos claasicos de sincronizacioon se basan en memoria compartida. La representacioo
n interna de un semaa foro o un monitor es compartida por los procesos que los usan. Sin
embargo, los procesos de un sistema distribuido no pueden compartir memoria, ya que, en
general, se ejecutan en procesadores diferentes. Se puede simular la memoria compartida en
sistemas distribuidos, pero es muy costoso. Por este motivo, la sincronizacioo n en sistemas
distribuidos se realiza mediante mecanismos basados en el paso de mensajes

Pregunta 2 (1 punto) Sept 2010


Comparar, desde el punto de vista de su generalidad para resolver problemas de sincronizacin, los
mecanismos semforo, cerrojo, regin crtica y regin crtica condicional.
El semforo es un mecanismo general, es decir, sirve para resolver cualquier problema de
sincroniza- cin. El cerrojo, sin embargo, est especficamente diseado para resolver el problema
de la exclusin mutua y no sirve para resolver la sincronizacin condicional. Que es lo mismo que
le ocurre, si bien en otro nivel de abstraccin, al mecanismo (lingstico) regin crtica, el cul
tambin solo puede resolver la exclusin mutua. La regin crtica condicional, sin embargo, es de
nuevo un mecanismo
general (y muy elegante), pero su implementacin exacta es desgraciadamente irrealizable.

10

Pregunta 2 (1 punto) Jun 2011


El concepto abstracto de monitor se puede realizar en Java de forma aproximada mediante una clase con
mtodos sincronizados. Explicar brevemente cules son las diferencias ms importantes entre esta forma
de realizacin y el el concepto de monitor.
El uso de mtodos sincronizados corresponde a la exclusin mutua entre las operaciones del
monitor. Sin embargo, hay que tener en cuenta lo siguiente:
En Java no se exige que todos los mtodos de la clase estn sincronizados. La definicin de
monitor exige que todas las operaciones (mtodos) se ejecuten en exclusin mutua.
En Java no se exige que los datos internos (fields) de los objetos la clase sean privados. La
definicin de monitor exige que los datos internos del monitor slo sean accesibles a travs de las
operaciones del monitor.
En Java no existen las variables de condicin. La sincronizacin condicional se realiza mediante los
mtodos wait(), notify(), y notifyAll(). La forma de distinguir entre distintas
condiciones consiste en comprobarlas de forma explcita en un bucle:
while (!condicin) wait();
ya que cuando una hebra invoca notify() o notifyAll() no se indica cul es la condicin que se
ha hecho verdadera.
Estas diferencias se pueden subsanar mediante una disciplina de programacin adecuada.

Pregunta 1 (1 punto) Jun 2010


Explicar brevemente cul es la diferencia entre los interbloqueos (deadlocks) y los bloqueos vivos
(livelocks).
Un interbloqueo es una situacin en la que dos o ms procesos concurrentes se encuentra bloqueados
esperando una accin de otros procesos del mismo grupo (por ejemplo, liberar un recurso), de tal
manera que se produce una dependencia circular entre ellos. En esta situacin los procesos estn en
estado suspendido y no pueden ejecutarse.
Un bloqueo vivo es una situacin en la que dos o ms procesos concurrentes intentan conseguir un
conjunto de recursos sin que ninguno de ellos los consiga, sin llegar a suspenderse. Por tanto, en una
situacin de bloqueo vivo los procesos implicados estn en estado de ejecucin y consumiendo tiempo
de procesador, sin poder avanzar.
Por tanto, la principal diferencia entre ambos tipos de bloqueos es que en el caso del interbloqueo los
procesos estn suspendidos y por tanto no se ejecutan, mientras que en el bloqueo vivo los procesos
afectados estn en ejecucin, aunque sin progresar.

Pregunta 3 (1 punto) Sept 2011


En la prctica 6 (Dibujo de imgenes contenidas en ficheros usando pipes) se hace, en un proceso hijo:
execlp("display", "display", "-", NULL);
1. Qu significa eso?
2. Qu instruccin o instrucciones hay que poner inmediatamente despues?
3. El padre debe crear un pipe, luego hacer el fork, y antes del execlp debe hacer algo con el pipe. qu es?

Que reemplaza su cdigo por el de un programa llamado display que debe encontrar en el
PATH al que le pasa su propio nombre y el carcter -, lo que significa que debe pintar una
imagen que se le pase por la entrada estndar.

Tratamiento de errores por fallo del execlp, ya que si tiene xito, no llega nunca a ejecutar
eso.

Redireccionar el extremo de escritura a la entrada estndar con dup2 y cerrar ambos extremos
del pipe.

11

Pregunta 4 (1 punto) Sept 2011


En la prctica 11 (Comunicaciones con RMI), qu hacen estas operaciones?:
1. rmic ServidorFicherosImpl
2. rmiregistry&
3. java -Djava.security.policy=permisosServidor.txt ServidorFicherosImpl

Crea el stub del servidor (ServidorFicherosImpl_Stub.class), que se encarga de


interpretar las peticiones del cliente y pasarlas a los mtodos correspondientes.

Los servicios de un servidor RMI deben registrarse para que los clientes los puedan encontrar.
Se registran en un proceso local independiente.

Ejecuta el servidor con una poltica de seguridad pasada en un fichero. En la prctica se permita
acceso indiscriminado.

Pregunta 2 (1 punto) Sept 2012


Es posible utilizar los cerrojos (como son los mutex de Posix) para resolver cualquier problema de
sincroni- zacin? Justificar al respuesta.
Los cerrojos permiten resolver los problemas de exclusin mutua, pero no los de sincronizacin
con- dicional (como ocurre en el problema del productor y el consumidor). Por tanto, no son un
mecanismo
de sincronizacin universal.

Pregunta 1 (1 punto) Jun 2004


Explicar brevemente cuaales son las condiciones que debe cumplir una solucion al problema de la
regio
n crtica para ser vaalida.
Una solucioon al problema de la regioon crtica debe cumplir las siguientes condiciones:
1. Exclusion mutua: Si un proceso se estaa ejecutando en su regioon crti- ca ninguun
otro proceso puede ejecutarse en su seccioon crtica.
2.

Progreso: Si ninguun proceso se estaa ejecutando dentro de su seccioon crtica y hay


uno o maas procesos que quieren entrar en sus respecti- vas secciones crticas,
soolo los procesos que no estaan en su seccioon no crtica (es decir, los que estaan
ejecutando el protoclo de entrada o el de salida) pueden participar en la
seleccioon del proceso al que se le permite entrar, y esta seleccioon no puede
postponerse indefini- damente.

3.

Espera acotada: Cuando un proceso estaa esperando entrar en su sec- cioon crtica,
el nuumero de veces que otros procesos pueden entrar
antes que e l estaa acotado.

12

PROGRAMAS
Pregunta 3 (1 punto) Sept 2012
Explique si el cdigo siguiente es reentrante y, si no lo es, modifquelo para que lo sea:
...
int aux;
void intercambia(int *a, int *b) { aux= *a;
*a= *b;
*b= aux;
}
...
Un subprograma es reentrante si se puede volver a invocar antes de que haya terminado de
ejecutarse.
Esta es una condicin necesaria para que se pueda llamar a un subprograma desde dos o ms
hebras concurrentes sin dar lugar a condiciones de carrera.
En el caso de C, para que un un subprograma sea reentrante debe usar slo variables locales y
parmetros. En este caso, el subprograma intercambia utiliza la variable global aux, por lo que no
es reentrante. Efectivamente, si dos hebras llaman concurrentemente a este subprograma puede
ocurrir que asignen valores a aux al mismo tiempo, con lo cual el valor de esta variable dependera
de la forma exacta en que se entrelace la ejecucin de la instruccin aux = *a por cada una de las
hebras.
La forma de corregir este problema en el ejemplo que se proporciona es sustituir la variable global
aux por una variable local. De esta manera, si varias hebras llaman a la vez a intercambia, cada
una de ellas utiliza su propia copia de aux almacenada localmente en su pila, y no hay condiciones
de carrera.
...
void intercambia(int *a, int *b) {
int aux;
aux= *a;
a= *b;
b= aux;
}...

Pregunta 4 (1 punto) sept 2012


Dado el programa siguiente, se pide justicar cuando terminara la ejecucin de los procesos que se crean.
#include <unistd.h>
#include <stdio.h>
void productor(int esc) { int i;
for (i=1; i<=5; i++) { write(esc, &i, sizeof i); sleep(1); }
if (execlp("display", "display", "figura.jpeg", NULL) == -1 ) write(2, "Error en la llamada a execlp\n",
29);
exit(0);
}
void consumidor(int lec) { int leidos, i;
while ((leidos = read(lec, &i, sizeof i)) > 0) { printf("Recibido %d\n", i);
}
exit(0);

13

}
int main(void) {
int pid, tubo[2]; pipe(tubo);
if ((pid= fork())==0) { consumidor(tubo[0]); } else { productor(tubo[1]); }
}
El programa Display, segn se invoca en el cdigo, muestra la imagen que se le pasa como
parmetro, hasta que el usuario finaliza su ejecucin.
El proceso productor termina cuando el usuario termina de ejecutar el programa
Display.
Segn la especificacin de POSIX, el proceso hijo no terminara nunca por que siempre habra un
descriptor de fichero abierto para escribir en la tubera. Este descriptor corresponde al mismo
proceso hijo, que nunca cierra su descriptor de escritura en la tubera. Por este motivo, es
conveniente cerrar los descriptores de ficheros de una tubera que un proceso no vaya a utilizar.
Sin embargo, en algunas implementaciones de los procesos de POSIX, cuando el proceso padre
ter- mina, se enva una seal a los hijos. Si esta seal no se trata, entonces se termina el proceso
hijo. En este caso, el proceso hijo del problema terminara cuando se completara la ejecucin del
programa Dispaly por parte del padre.
Dada la complejidad y nivel de detalle necesario conocer para identificar estos casos, se considerarn ambas como aceptables. Sin embargo, la primera es la vlida si se considera nicamente el
comportamiento de las tuberas.

Pregunta 3 Jun 2013


Dado el siguiente programa:
1
2.
3.

# i n c l u d e < u n i s t d . h>
# i n c l u d e < s t d i o . h>

4
5
6
7

i n t main ( v o i d ) {
i n t p1 [ 2 ] , p2 [ 2 ] ;
int i ;

8
9

p i p e ( p1 ) ; p i p e ( p2 ) ;

10
7
8
9
10

i f ( fork ()==0) {
/ / Proceso 1
c l o s e ( p1 [ 1 ] ) ;
c l o s e ( p2 [ 0 ] ) ;

15
11
12
13
14
15
16
17
18
19
20
21

w h i l e ( r e a d ( p1 [ 0 ] , &i , s i z e o f i ) > 0 ) {
i = i i;
w r i t e ( p2 [ 1 ] , &i , s i z e o f i ) ;
}
exit (0);
}
else i f ( fork ()==0) {
/ / Proceso 2
c l o s e ( p1 [ 0 ] ) ;
c l o s e ( p1 [ 1 ] ) ;
c l o s e ( p2 [ 1 ] ) ;

27
14
15
16
17
18
19
20
21
22
23
24

w h i l e ( r e a d ( p2 [ 0 ] , &i , s i z e o f i ) > 0 ) {
i = i + 1;
p r i n t f ( " R e c i b i d o %d \ n " , i ) ;
}
exit (0);
}
else {
/ / Proceso 3
c l o s e ( p1 [ 0 ] ) ;
c l o s e ( p2 [ 0 ] ) ;
c l o s e ( p2 [ 1 ] ) ;

39
40
41

f o r ( i = 1 ; i < = 5 ; i ++) {
w r i t e ( p1 [ 1 ] , &i , s i z e o f i ) ;

14

sleep ( 1) ;

42

43

exit (0);

44

45
46

Contestar a las siguientes cuestiones:

Subpregunta 3.1 (0,5 puntos)


Cul ser la salida en pantalla al ejecutar este programa?
Recibido 2
Recibido 5
Recibido 10
Recibido 17
Recibido 26

Subpregunta 3.2 (0,5 puntos)


En qu orden terminarn los procesos? Justificar la respuesta
En primer lugar termina el proceso 3. Como no lee de la tubera no tiene que esperar por otro. Cuando
termina el proceso 3, se cierra su descriptor p1[1], lo que provoca el final del proceso 1. Cuando termina
el proceso 1, se cierra su descriptor p2[1], lo que provoca el final del proceso 2.

Subpregunta 3.3 (0,5 puntos)


Qu ocurre si se elimina la sentencia de la lnea 38? Justificar la respuesta.
Este cambio no afecta al comportamiento del programa, ya que cuando el proceso 3 ejecuta la sen- tencia
exit(0) se cierran todos sus descriptores de ficheros y los procesos acaban como en el caso anterior.

Subpregunta 3.4 (0,5 puntos)


Qu ocurre si se elimina la sentencia de la lnea 25? Justificar la respuesta
Este cambio hace que los procesos 1 y 2 no terminen nunca. Al dejar abierto el descriptor p1[1] del
proceso 2, el proceso 1 no terminar nunca, ya que quedar un descriptor de escritura en la tubera p1
abierto. Como consecuencia, ste no cerrar su descriptor p2[1], lo que provocar que el proceso
2 termine, por un razonamiento anlogo al anterior.

15

16

Problemas Regin crtica


Pregunta 2 (1 punto) Jun 2008
En una maaquina monoprocesador existe una instruccioo n ensamblador SWAP Var1, Var2 que
inter- cambia los valores de sus dos paraametros, y deja la palabra de control y estado de la maaquina
la que regula los saltos condicionales seguun el valor del segundo paraametro antes de realizarse la
operacio
on.
Se pide programar con esta instruccioo n (y otras instrucciones claasicas de ensamblador que
resulten necesarias) una solucioon (con espera activa) que garantice exclusioon mutua en el problema
de la regioon crtica.
Cerrojo:
Local:

Word
Word

1
0

-- Variable global, 1 significa abierto


-- Variable local a un proceso

$1 SWAP
Local,
$1
Cerrojo BZ
Region cr
tica
CLEAR Local
...
MOVI
#1,
Cerrojo Resto
del proceso

Pregunta 2 (1,5 puntos) Jun 2009


A continuacin se muestra el algoritmo de Peterson modificado, que trata de resolver el problema de
la regin crtica entre dos procesos, sin apoyo del sistema operativo. La modificacin consiste en
mover la sentencia turn = j desde el prlogo (en el cdigo aparece comentada) al eplogo. Esta
solucin es incorrecta. Se pide demostrarlo exhibiendo un contraejemplo.
boolean flag[2];
flag [0] = false;
flag [1] = false;
int turn = i;
while (true) {
flag [i] =
true;
// turn = j;
while (flag [j] && turn == j); // espera activa
regin crtica
flag [i] =
false; turn = j;
resto del cdigo
}

Nota: Este extracto de cdigo pertenece al proceso i. El cdigo que ejecuta el proceso j es el mismo,
cambiado en el texto las i por j y vice versa.
Supongamos la situacin inicial. Si intenta entrar el proceso j, lo podr hacer dado que flag[i]
vale false. Si mientra est este proceso en su regin crtica hay un cambio de contexto e intenta
entrar el proceso i, tambin lo lograr, ya que turn vale i y, por tanto, la evaluacin de la
condicin del bucle while interno tendr el valor false

17

Por consiguiente no se cumple la exclusin mutua.

Pregunta 2 (1 punto) Jun 2010


El siguiente intento de solucin en ensamblador de un buffer de un nico elemento sincronizado en
los dos sentidos, tiene un problema de carrera que causa que el programa no sea correcto.
Se pide realizar la modificacin mnima que solucione tal problema
bandera1
bandera2

word
word

0
0

-- PRODUCTOR
$1
-- producir siguiente
elemento
$2
TST
bandera1, 1
BRNZ $2
-- introducir elemento en
buffer
CLEAR
bander
a2
GOTO
$1
-- CONSUMIDOR
$3
TST
bandera2, 1
BRNZ $3
-- extraer elemento del
buffer CLEAR
bandera1
-- consumir siguiente
elemento GOTO $3

Notas:
BRNZ: Instruccin de salto condicional. Se produce el salto si el contenido de la palabra de control y
estado del computador es distinto de cero
TST: Instruccin de test-and-set.
CLEAR: Asigna el valor 0 a su argumento.
El problema de esta solucin se debe al valor inicial de la variable bandera2. En la situacin inicial,
si el consumidor ejecuta primero, podr acceder al buffer, aunque est vaco. La
solucin al problema consiste en iniciar la variable bandeja2 con el valor 1:
bandera2 word 1

Pregunta 1 (1 punto) Jun 2011


La siguiente propuesta de solucin al problema de la exclusin mutua no es tal, sino que falla (a
pesar de que en su dia apareci publicada en una revista cientfica como correcta!). Se pide demostrar
su incorreccin a base de exhibir un contraejemplo (escenario de fallo).
1.
2.

/ / V a r i a bl e s comp art idas e n t r e l a s dos hebras


Boolean [ ] f l a g = { f a l s e , f a l s e } ;
i n t t u r n = 0 ; / / S o l o toma l o s v a l o r e s 0 o 1

4
5
6

/ / C o d i g o p a r a d o s h e b r a s i ( i = 0 o 1 , j = 1 i ) }
p u b l i c void c o di go H e br a I ( ) {

7
8

/ / E s t e c o d i g o l o e j e c u t a l a h e b r a i =0

9
22
23

int i = 0;
int j = 1 i ;

18

13
25

while ( true ) {
f l a g [ i ]= true ;
while ( t u r n != i ) {
while ( f l a g [ j ] ) {
turn = i ;
}
/ / C o d i g o s e c c i on c r i t i c a

26
27
28
29
30
20

};

21

flag [ i ] = false ;

22
23

/ / C odigo r e s t o d e l c i c l o
}

47
48
49

Supongamos que turno vale uno y el proceso cero avanza, desde el principio de su cdigo, hasta
justo el principio de la instruccin de asignacin que va a poner el valor cero en la variable
turno, pero dejando en sta an el valor uno.
A continuacin se produce una multiplexacin y, siendo an uno el valor de turno, el proceso
uno puede avanzar libremente desde el principio, hasta introducirse en su regin crtica.
Y si estando el proceso uno en su regin crtica se produce otra multiplexacin, el proceso cero
pondr el valor cero en la variable turno y, justo a continuacin, entrar en su propia regin
crtica.
Con lo que se habr violado la exclusin mutua, Q.E.D.

Pregunta 1 (1 punto) Sept 2011


La siguiente propuesta de solucin al problema de la exclusin mutua no es tal, sino que
falla violando la exclusin mutua (a pesar de que en su dia apareci publicada en una revista
cientfica como correcta!).
En efecto, si por ejemplo turno vale uno y el proceso cero avanza desde el principio de su
cdigo hasta justo el principio de la instruccin de asignacin que va a poner el valor cero en la
variable turno, pero dejando en sta an el valor uno, y a continuacin se produce una
multiplexacin, siendo an uno el valor de turno, el proceso uno puede avanzar libremente desde el
principio, hasta introducirse en su regin crtica.
Y si estando el proceso uno en su regin crtica se produce otra multiplexacin, el proceso cero
pondr el valor cero en la variable turno y, justo a continuacin, entrar en su propia regin crtica.
Con lo que se habr violado la exclusin mutua.
Se pide demostrar que el algoritmo falla tambien en cuanto a ausencia de espera acotada, a base de
exhibir un contraejemplo (escenario de fallo, algo como lo que se ha dicho anteriormente para la
exclusin mutua).

19

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

/ / V a r i a b l e s c o m p a r t i d a s e n t r e l a s dos hebras
Boolean [ ] f l a g = { f a l s e , f a l s e } ;
i n t t u r n = 0 ; / / S l o toma l o s v a l o r e s 0 o 1
/ / Cdigo pa ra do s h e b r a s
p u b l i c void c o d i g o H e b r a I ( )
/ / E s t e cdigo l o
int i = 0; int j =

i ( i = 0 o 1 , j = 1 i ) }
{
e j e c u t a l a h e b r a i =0
1 i;

while ( true ) {
f l a g [ i ]= true ;
while ( t u r n != i ) {
while ( f l a g [ j ] ) {
turn = i ;
}

};

/ / C di go s e c c i n c r t i c a
flag [ i ] = false ;
/ / Cdi go r e s t o d e l c i c l o

Se puede producir espera no acotada cuando, estando cualquiera de los dos procesos esperando
en el tercer bucle a que el "flag"del otro (que est en su seccin crtica) sea falso, ste otro sale y
entra de nuevo en su seccin crtica tan rpidamente (o las multiplexaciones se producen en tales
instantes de tiempo), que el primero de ellos no llega a percibir que el "flag"del otro ha tenido en
algn momento el valor falso.
Y lo anterior puede repetirse cualquier nmero de veces

Pregunta 1 (1 punto) Sept 2012


Cuando dos procesos comparten dos regiones crticas anidadas (es decir, una de ellas contenida
dentro de la otra), puede suceder que, si se programa errneamente, el programa concurrente resultante
tenga riesgo de interbloqueo.
Se pide programar un ejemplo de lo dicho, utilizando regiones crticas como mecanismo de
programacin.
- - Process A
...
region X do
...
region Y do
...
end region
...
end region
...

- - Process B
...
region Y do
...
region X do
...
end region
...
end region
...

20

Pregunta 2 (1 punto) Jun 2013


La siguiente solucin al problema de la exclusin mutua no es tal, sino que falla (!a pesar de que
en su dia apareci publicada en una revista cientfica como correcta!). Se pide demostrar su
incorreccin a base de exhibir un contraejemplo (escenario de fallo; pista: viola la exclusin mutua).
flag: array[0..1] of Boolean;{inicialmente false}
turn:

0..1;

{programa para proceso i (i = 0 o 1, j = 1-i)}


repeat
flag[i]:= true;
while turn <> i do begin
while flag[j] do skip;
turn:= i; end;
...
seccion critica
...
flag[i]:= false;
...
resto del ciclo
...
until false;

Inicialmente los dos flag son falsos y el valor de turn es uno.


El proceso cero pone entonces su flag a cierto y, justo antes de poner turno a cero, el proceso uno pone
su flag a cierto y, a continuacin, entra directamente en su regin crtica. E inmediatamente despus, el
proceso cero poner turn a cero y entra en su propia regin crtica, con lo que se viola la exclusin
mutua.

21

22

Productor-consumidor
Pregunta 3 (2,5 puntos) Sept 2009
Se ha modificado la cola sincronizada de la prctica del Productor-Consumidor con variables
condicin para que realice una cola que transforme una seal continua, muestreada a una frecuencia,
en otra seal similar mues- treada a una frecuencia tres veces superior. La seal de salida se obtiene
por interpolacin lineal. Las estructuras de datos son las mismas, salvo Segundo, que indica el segundo
ms viejo en la cola:
1. # d e f i n e
2. # i n c l u d e
3. # i n c l u d e
4. # i n c l u d e

_REENTRANT
< s t d i o . h>
< s t d l i b . h>
< p t h r e a d . h>

5
7
8
9
10

# define
# define
# define
# define

P e r i o d o P r o d u c t o r 3000000
P e r i o d o C o n s u m i d o r 1000000
Cantidad 3
Longitud 5

10
24
25
26
27
28

double Caj a [ L ongitu d ] ;


u n s i g n e d i n t P r i m e r o = 0 , S egundo = 1 , U l t i m o = 0 , Numero = 0 , P e r i o d o = 0 ;
p t h r e a d _ m u t e x _ t c e r r o j o = PTHREAD_MUTEX_INITIALIZER;
p t h r e a d _ c o n d _ t h u e c o s = PTHREAD_COND_INITIALIZER ;
p t h r e a d _ c o n d _ t e l e m e n t o s = PTHREAD_COND_INITIALIZER ;

Y de las funciones, slo cambia la de tomar datos:


31

32
33

I n s e r t a un v a l o r X en l a c o l a . S i l a c o l a e s t l l e n a , e s p e r a .
Nor m alm ente e s l l a m a d a p o r un n i c o p r o c e s o con f r e c u e n c i a f .
Es e x a c t a m e n t e i g u a l que e l Pon de l a p r c t i c a . /

4
50
51
52
53
54
55
56
57

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

v o i d P o n L e n t o ( d o u b l e X) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o ) ;
w h i l e ( Numero == L o n g i t u d ) p t h r e a d _ c o n d _ w a i t (& h u e c o s , &c e r r o j o ) ;
C a j a [ U l t i m o ] = X ; U l t i m o = ( U l t i m o + 1 ) % L o n g i t u d ; Numero + + ;
p t h r e a d _ c o n d _ s i g n a l (& e l e m e n t o s ) ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o ) ;
return ;
}
/

R ecoge v a l o r i n t e r p o l a d o ms a n t i g u o . E s p e r a h a s t a que haya d a t o s de e n t r a d a


s u f i c i e n t e s p a r a i n t e r p o l a r . Es l l a m a d a n o r m a l m e n t e p o r un n i c o p r o c e s o con
f r e c u e n c i a 3 f . La i n t e r p o l a c i n s e h a c e t e n i e n d o en c u e n t a que en cada
p e r i o d o l e n t o hay t r e s p e r i o d o s r p i d o s . /

v o i d TomaRapido ( d o u b l e pX ) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o ) ;
i f ( P e r i o d o == 0 ) {
/ E l p r i m e r v a l o r de l a c o l a ( e l ms v i e j o ) /
w h i l e ( Numero < 1 ) p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s , &c e r r o j o ) ;
pX = C a j a [ P r i m e r o ] ;
}
e l s e i f ( P e r i o d o == 1 ) { / 2 / 3 d e l p r i m e r v a l o r + 1 / 3 d e l s e g u n d o /
w h i l e ( Numero < 2 ) p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s , &c e r r o j o ) ;
pX = ( 2 . 0 C a j a [ P r i m e r o ] + C a j a [ S egundo ] ) / 3 . 0 ;
}
else {
/ 1 /3 d e l primer v a l o r + 2 /3 d e l segundo /
w h i l e ( Numero < 2 ) p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s , &c e r r o j o ) ;
pX = ( C a j a [ P r i m e r o ] + 2 . 0 C a j a [ S egundo ] ) / 3 . 0 ;
P r i m e r o = S egundo ; S egundo = ( S egundo + 1 ) % L o n g i t u d ; Numero;
p t h r e a d _ c o n d _ s i g n a l (& h u e c o s ) ;
}
P e r i o d o = ( P e r i o d o +1) % 3 ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o ) ;
return ;
}

Tenemos dos procesos de prueba:

23

1
2
3
4
5
6
7
8
9
10
11
12

void P ro d u ct o r ( void pId ) {


int i ;
f o r ( i = 0 ; i < C a n t i d a d ; i ++) {
PonL ento ( i ) ;
p r i n t f ( " P r o d u c t o r p r o d u c e %5.3 f \ n " , ( d o u b l e ) i ) ;
usleep ( PeriodoProductor ) ;
}
r e t u r n NULL ;
}
v o i d Cons um idor ( v o i d p I d ) {
d o u b l e D ato ; i n t j ;

f o r ( j = 0 ; j < C a n t i d a d 3 ; j ++) {
TomaRapido(& D ato ) ;
p r i n t f ( " Cons um idor consume %5.3 f \ n " , D ato ) ;
u s le e p ( PeriodoConsumidor ) ;
}
r e t u r n NULL ;

13
14
15
16
17
18
19

Que se arrancan y se espera a que terminen como de costumbre:


1
2
3
4
5
6
7
8

i n t main ( v o i d ) {
pthre ad_t productorid , consumidorid ;
p t h r e a d _ c r e a t e (& p r o d u c t o r i d , NULL, P r o d u c t o r , NULL ) ;
p t h r e a d _ c r e a t e (& c o n s u m i d o r i d , NULL, Cons um idor , NULL ) ;
p t h r e a d _ j o i n ( p r o d u c t o r i d , NULL ) ;
p t h r e a d _ j o i n ( c o n s u m i d o r i d , NULL ) ;
exit (0);
}

Tal como est, la prueba ha dado el siguiente resultado:


---- Productor produce 0.000
Consumidor consume 0.000
Productor produce 1.000
Consumidor consume 0.333
Consumidor consume 0.667
Consumidor consume 1.000
Productor produce 2.000
Consumidor consume 1.333
Consumidor consume 1.667
Consumidor consume 2.000

****
---****
****
****
---****
****
****

Subpregunta 3.1 (0.5 puntos)


Acaba el programa de prueba o no? Explquelo en detalle.
No, porque para sacar los valores mayores que 2, necesita que se introduzca el 3 para
poder interpo- lar. Concretamente estamos en el periodo 1, esperando a que haya dos
nmeros que no hay.

Subpregunta 3.2 (0.5 puntos)


Escriba la traza que saldra si cambiamos el periodo del productor a 100000 (# define
explicndolo.

PeriodoProductor 100000),

Ahora va mucho ms deprisa que el consumidor y adems tiene sitio para todos sus datos:
---- Productor produce 0.000
**** Consumidor consume 0.000
---- Productor produce 1.000
---- Productor produce 2.000
**** Consumidor consume 0.333
**** Consumidor consume 0.667
**** Consumidor consume 1.000
**** Consumidor consume 1.333
**** Consumidor consume 1.667
**** Consumidor consume 2.000

24

Subpregunta 3.3 (0.5 puntos)


Escriba la traza que saldra si cambiamos el periodo del productor a 1000000 (# define
explicndolo.

PeriodoProductor 1000000),

Ahora, al ir a la misma velocidad, se entrelazan:


---- Productor produce 0.000
Consumidor consume 0.000
Productor produce 1.000
Consumidor consume 0.333
Productor produce 2.000
Consumidor consume 0.667
Consumidor consume 1.000
Consumidor consume 1.333
Consumidor consume 1.667
Consumidor consume 2.000

****
---****
---****
****
****
****
****

Subpregunta 3.4 (0.5 puntos)


Explique para qu sirve el # define
debe ser reentrante?

_REENTRANT.

Qu es ser reentrante? Qu funcin de las que ve

Sirve para elegir versiones reentrantes de las funciones de biblioteca. Ser reentrante es
poder ser invocado durante la ejecucin de una invocacin, sin que ello afecte a los
resultados. printf y usleep debe ser reentrante, porque la llaman las dos hebras.

Subpregunta 3.5 (0.5 puntos)


Cambie TomaRapido suponiendo que ahora va a frecuencia doble que PonLento. Puede hacerlo como
modificacio- nes al TomaRapido dado o simplemente una explicacin verbal.
Ahora slo hay dos periodos. El primero se trata igual y el segundo es como el tercero de
antes, pero entregando la mitad de la suma. El segundo de antes desaparece:
1

e l s e i f ( P e r i o d o == 1 ) { / 1 / 2 d e l p r i m e r v a l o r + 1 / 2 d e l s e g u n d o /
pX = ( Caja [ P r i m e r o ] + Caja [ Segundo ] ) / 2 . 0 ;
P e r i o d o = ( P e r i o d o +1) % 2 ;

Completo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

v o i d TomaRapido ( d o u b l e pX ) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o ) ;
i f ( P e r i o d o == 0 ) {
/ E l p r i m e r v a l o r de l a c o l a ( e l mas v i e j o ) /
w h i l e ( Numero < 1 ) p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s , &c e r r o j o ) ;
pX = Caja [ P r i m e r o ] ;
}
e l s e i f ( P e r i o d o == 1 ) { / 1 / 2 d e l p r i m e r v a l o r + 1 / 2 d e l s e g u n d o /
w h i l e ( Numero < 2 ) p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s , &c e r r o j o ) ;
pX = ( Caja [ P r i m e r o ] + Caja [ Segundo ] ) / 2 . 0 ;
P r i m e r o = Segundo ; Segundo = ( Segundo + 1 ) % L o n g i t u d ; Numero;
p t h r e a d _ c o n d _ s i g n a l (& h u e c o s ) ;
}
P e r i o d o = ( P e r i o d o +1) % 2 ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o ) ;
return ;
}

25

MONITORES

INDICE
Jun 2006: Sistema de seguridad de un puente que controla el peso mximo de los vehculos y el
nmero de vehculos atravesndolo. Prioridad a las ambulancias.
Sept 2006: Gestin de la atencin de un administrativo (hebra) a dos ventanillas a las que llega
gente. El administrativo debe atender a la ventanilla que tenga ms gente esperando.
Jun 2007: Gestor de piezas disponibles en dos cubos, se controla la capacidad de los cubos y se
controlan los pedidos de las piezas.
Sept 2007: Gestin del control de temperatura y personas en una sala. Hebra control de
personas y hebra para control de temperatura.
Jun 2008: Monitor C: Reloj con tres entradas (ha pasado un segundo, lectura de reloj, esperar
cambio de hora). Mismo monitor en Java.
Sept 2008: Gestin de un evento (activo o inactivo), hebra para el evento y un control del
nmero de veces que sucede el cambio para dar permiso a otras hebras que dependen del
evento.
Jun 2009: Ordenar el acceso de un conjunto de hebras a un recurso. Se le permite el acceso a la
que tenga el nmero ms bajo.
Sept 2009: Controlar las hebras que obtienen datos de sensores, deshacindose de aquellas que
difieran ms del 10% el valor promedio.
Jun 2010: Control de hebras que toman medidas durante un ciclo, la que tome la medida ms
alta es seleccionada.
Sept 2010: Control del nivel de agua y gas de una mina, una hebra controla el motor, otra el
nivel de gas y otra lee el nivel de agua.
Jun 2011: Ordenar el acceso de hebras a un recurso compartido, se da permiso mediante turnos
controlados por un temporizador.
Sept 2011: Gestin de subastas. Una hebra gestora y unas hebras compradoras que hacen pujas
por un lote. Las pujas se finalizan cuando se produce una puja igual a la cantidad de precio
fijada, o cuando el gestor decide finalizarla (la mayor oferta gana).
Jun 2012: Gestin de los despegues de aviones en un aeropuerto, aviones VIPs que tienen ms
prioridad y aviones normales, los despegues se controlan con un temporizador.
Sept 2012: Gestin de 4 ascensores (matrices), el ms cercano debe acudir a la llamada.
Dic 2012: Gestin de actividades de unas hebras cliente con trabajos pendientes y unas hebras
trabajadoras que llevan a cabo dichos trabajos. Las hebras trabajadoras se asignan con
prioridades.
Jun 2013: Gestin de un sistema de mensajes, hebras emisoras envan mensajes que deben
leer todas las hebras receptoras.

Jun 2006 Pregunta 5 (3 puntos)


Se quiere desarrollar un sistema de seguridad que impida que un puente cargue maas
de 15.000 Kg y no haya simultaaneamente maas de 10 vehculos atravesaandolo.
Cuando un vehculo quiere entrar en el puente, ejecuta el meetodo entrarPuente.
Cuando abandona el puente, ejecuta el meetodo salirPuente. Ambos meetodos tienen
como argumento el peso del vehculo correspondiente. El primer meetodo, ademaas,
tiene un argumento booleano
que indica si el vehculo es una ambulancia. El perfil de estas operaciones:
... void entrarPuente(int Peso, boolean esAmbulancia)
... void salirPuente(int Peso)
Un vehculo no recibiraa permiso para entrar en el puente si dadas sus caractersiticas
o el estado del mismo, se incumplen los requisitos de seguridad. Ademaas, las
ambulancias tienen prioridad para acceder al puente respecto al resto de los vehculos.
Se debe desarrollar un monitor que proporcione las dos operaciones previas seguun se
han descrito y que garantice exclusioo n mutua en el acceso a los datos compartidos.
public class puenteSeguro {
private
private
private
private
private

int pesoEnPuente = 0;
int nVehiculos
= 0;
int nAmbulanciasEsperando = 0;
static final int pesoMaximo = 15000;
static final int nVehiculosMaximo = 10;

public synchronized void entrarPuente(int Peso, boolean esAmbulancia) throws


InterruptedException {
if (esAmbulancia) nAmbulanciasEsperando ++;
while((Peso + pesoEnPuente > pesoMaximo)
| | (nVehiculos + 1 > nVehiculosMaximo)
| | (nAmbulanciasEsperando > 0 && !esAmbulancia)) wait();
if (esAmbulancia) nAmbulanciasEsperando ;
pesoEnPuente = pesoEnPuente + Peso;
nVehiculos++;
}
public synchronized void salirPuente(int Peso) throws InterruptedException {
pesoEnPuente = pesoEnPuente Peso;
nVehiculos;
notifyAll();
}
}

Sept 2006 Pregunta 4 (2.5 puntos)


Una oficina dispone de dos ventanillas y un soo lo administrativo para atender a los ciudadanos.
Cuando un ciudadano llega, espera en una de las ventanillas, hasta que le atiende (desbloquea) el
administrativo. El administrativo atiende a un ciudadano y descansa. Si no hay ciudadanos esperando,
espera a que llegue alguno y le desbloquee. El administrativo atiende primero a un ciudadano de la
ventanilla en la que hay maas esperando. Si hay igual nuu mero de ciudadanos, puede elegir cualquier
ventanilla para atender.
Se pide desarrollar un monitor en Java u objeto protegido que sincronize a los procesos ciudadano y
al administrativo, seguun la especificacioon anterior, con las siguientes operaciones:
EsperarVentanilla1: esta operacioon la ejecuta un proceso ciudadano cuando quiere esperar en
la ventanilla 1.
EsperarVentanilla2: esta operacioon la ejecuta un proceso ciudadano cuando quiere esperar en
la ventanilla 2.
AtenderCiudadano: esta operacioon la ejecuta el proceso administrativo cuando va a atender
a un ciudadanos.
No es necesario garantizar que el orden en que se atiende a los ciudadanos coincide con el orden en que
llegan.
public class oficina {
private
private
private
private
private

static final int nMaxSeguidos = 5;


int nEsperaV1 = 0;
int nEsperaV2 = 0;
boolean permisoV1 = false;
boolean permisoV2 = false;

/////////////////////////////////////////////////////
public synchronized void EsperarVentanill1()
throws InterruptedException{
if ((nEsperaV1 == 0) && (nEsperaV2 == 0)) notifyAll();
nEsperaV1 ++;
while (!permisoV1){ wait();}
nEsperaV1 ;
permisoV1 = false;
}
/////////////////////////////////////////////////////
public synchronized void EsperarVentanill2()
throws InterruptedException{
if ((nEsperaV1 == 0) && (nEsperaV2 == 0)) notifyAll();
nEsperaV2 ++;
while (!permisoV2){ wait();}
nEsperaV2 ;
permisoV2 = false;
}
/////////////////////////////////////////////////////
public synchronized void atenderCiudadano()
throws InterruptedException{
while (nEsperaV1 == 0 && nEsperaV2 == 0){
wait();
}
permisoV1 = (nEsperaV1 >= nEsperaV2);
permisoV2 = (nEsperaV2 > nEsperaV1);
notifyAll();
}
}

Jun 2007

Pregunta 4 (3 puntos)

Un sistema de fabricacio
on tiene una lnea de produccio
n de piezas rojas y otra de piezas azules, que almacena en dos
o
cestos, uno para cada color, de capacidad limitada. Ademaas, hay un sistema de gestio
n automaatico de pedidos se encarga de
o
extraer de los cestos el nu
mero de piezas de cada color solicitado, y empaquetarlas.
u
Un computador controla estos sistemas y un monitor (GestorDePiezas) gestiona el nu
mero de piezas
u
almacenadas en los cestos. Hay una hebra asociada a cada lnea de produccio
n. Cuando se completa la
o
produccioon de una pieza, se almacena en el cesto correspondiente y se activa la hebra correspondiente que, entre otras acciones,
incrementa el nu
mero de piezas utilizando un meetodo del monitor.
u
Cada vez que se recibe un pedido, se crea una hebra encargada de gestionarlo. Una de las acciones que realiza es solicitar al
monitor el nu
mero de piezas especificado en el pedido.
u
Se pide programar el monitor GestorDePiezas que proporcione los siguientes meetodos:

adirAzul: Estos meetodos no tienen paraametros. Los llama la hebra correspon- diente
n
a
nadirRoja y a
cuando se produce una pieza nueva de un color determinado.

mero de pie- zas


u
solicitarPiezas (int SolRojas, int SolAzules): Este meetodo indica el nu
de cada color de un pedido.

El comportamiento del monitor debe cumplir los siguientes requisitos:

Cuando se llama a uno de los meetodos que notifican que se ha an


adido una pieza, se incrementa el contador
n
de nu
mero de piezas correspondiente.
u

Si despuees de an
adir la pieza el cesto estaa lleno, entonces se bloquea a la hebra hasta que se libere espacio.
n
La consecuencia de este bloqueo es que no se seguiraan produciendo piezas de ese color.

Cuando se recibe un pedido, se comprueba si hay piezas suficientes en el cesto. Si hay piezas, se actualizan
los datos del monitor y la hebra continu
a con el procesamiento del pedido.
u

Si no hay piezas suficientes, se bloquea a la hebra solicitante hasta que las haya.

Si hay una hebra que ha solicitado un pedido esperando a que se complete, las hebras que lleguen con
pedidos nuevos se deben bloquear hasta que se haya satisfecho aqueel. Entonces, se procesaraa uno nuevo (no es
necesario mantener ninguu
n orden en el procesamiento de estos pedidos nuevos).

public class GestorDePiezas {


final static private int NMaxRojas
= 50;
final static private int NMaxAzules = 50;
private int NRojas
= 0;
private int NAzules = 0;
private boolean PedidoEnCurso = false;
public synchronized void AnadirRoja()throws InterruptedException {
NRojas++;
notifyAll();
while (NRojas == NMaxRojas) wait();
}
public synchronized void AnadirAzul()throws InterruptedException {
NAzules++;
notifyAll();
while (NAzules == NMaxAzules) wait();
}
public synchronized void SolicitarPedido(int SolRojas, int SolAzules) throws InterruptedException {
while (PedidoEnCurso) wait();
PedidoEnCurso = true;
while (SolRojas > NRojas | | SolAzules > NAzules) wait();
NRojas = NRojas SolRojas;
NAzules = NAzules SolAzules;
PedidoEnCurso = false;
notifyAll();
}
}

Sept 2007

Pregunta 4 (3 puntos)

Se quiere desarrollar un sistema para controlar la temperatura y el nu


mero de personas que se encuen- tran en una sala de un
u
museo. En condiciones normales, se permiten 50 personas en la sala. Si la temperatura sube por encima de un umbral (tUmbral
= 30), se limita el nu
mero de personas a 35. Si cuando se detecta este suceso el nu
u
mero de personas en la sala es mayor que 35,
u
no es necesario desalojarlas.
Una hebra de control (ControlTemperatura) se encarga de controlar un sistema de refrigeracio
on que reduce la
temperatura de la sala. Esta hebra se activa cuando se detecta una temperatura mayor que el umbral.
Se pide desarrollar un monitor (GestorSala) que sincronice a las hebras que representan personas y a la hebra
ControlTemperatura, de acuerdo a las especificaciones previas. El monitor debe proporcionar los siguientes meetodos:
entrarSala: se invoca cuando una persona quiere entrar en la sala. Al principio de este meetodo se debe comprobar si
la temperatura excede el umbral y activar la hebra de control, si fuera necesario.
salirSala: se invoca cuando una persona quiere salir de la sala. En este meetodo no es necesario comprobar el
valor de la temperatura.
esperarARefrigerar: la hebra ControlTemperatura invoca este meetodo cuando estaa lista para realizar su
funcio
n. Debe esperar a que la temperatura sobrepase el umbral.
o
finRefrigerar: la hebra ControlTemperatura invoca este meetodo cuando consigue que la tem- peratura estee
por debajo del umbral.
La temperatura de la sala se puede consultar invocando al meetodo valorTemperatura proporcionado por el objeto
sensorT que devuelve un valor entero.
No es necesario garantizar que el orden de acceso a la sala coincide con el orden de llegada a la puerta de entrada.
public class gestorSala {
private int nPersonas = 0;
private int nMaxPersonasNormalT = 50;
private int nMaxPersonasAltaT
= 30;
private int nMaxPersonas = nMaxPersonasNormalT;
private boolean tAlta = false; private
int tUmbral = 30;
public synchronized void entrarSala () throws InterruptedException{
// Activar solo una vez la tarea de control i
if (sensorT.valorTemperatura > tUmbral && !tAlta){
tAlta = true;
nMaxPersonas = nMaxPersonasAltaT;
// Para despertar a la hebra de control notifyAll();
}
while (nPersonas >= nMaxPersonas) wait();
nPersonas++;
}
public synchronized void salirSala () throws InterruptedException{
nPersonas;
if (nPersonas < nMaxPersonas) notifyAll();
}
public synchronized void esperarARefrigerar () throws InterruptedException {
while (!tAlta) wait();
}
public synchronized void finRefrigerar () throws InterruptedException {
nMaxPersonas = nMaxPersonasNormalT;
tAlta = false;
if (nPersonas < nMaxPersonas) notifyAll();
}
}

Jun 2008

Pregunta 4

Subpregunta 4.1 (2.5 puntos)


Escriba un monitor para pthreads que represente un reloj y tenga tres entradas: una para indicar que ha pasado un
segundo, otra para leer el reloj (horas, minutos y segundos, sin condiciones de carrera), y otra para esperar al siguiente
cambio de hora, es decir, a que los minutos y los segundos pasen a valer cero. La interfaz, reloj.h es la que sigue:
typedef struct { int hora;
int minuto;
int segundo;
} tiempo;
void otro_segundo(void); /* Sucede un nuevo segundo */
tiempo lee(void);
void espera_hora(void);

/* Lee el reloj */
/* Espera el siguiente cambio de hora */

#define _REENTRANT
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "reloj.h"
pthread_mutex_t cerrojo =
PTHREAD_MUTEX_INITIALIZER; pthread_cond_t
nueva_hora = PTHREAD_COND_INITIALIZER;
tiempo T;
int esperando=0;
void otro_segundo(void) { /* Sucede un nuevo
segundo */ int i;
pthread_mutex_lock (&cerrojo);
/* Los campos de T deben actualizarse at
micamente
o
T.segundo = (T.segundo + 1) % 60;
/* Sin pasar de 60 los segundos */
if (T.segundo == 0) {
T.minuto = (T.minuto + 1) % 60;
/* ni los minutos */
if (T.minuto == 0) {
T.hora = (T.hora + 1) % 24;
/* ni de 24 las horas */
/* Al cambiar la hora debo despertar a */
while (esperando > 0) {
pthread_cond_signal(&nueva_hora);/* TODOS los que estaban esperando ese cambio */
esperando--;
}
}
}
pthread_mutex_
unlock(&cerroj
o); return;
}
/* Lee el reloj */
tiempo lee(void) {
tiempo t;
pthread_mutex_lock (&cerrojo); /* La lectura ha de hacerse en exclusi
on mutua */
t= T;
/* sobre una copia */
pthread_mutex_unlock(&cerrojo);
return t;
/* para devolver la copia tras liberar el cerrojo */
}
void espera_hora(void) { /* Espera el siguiente cambio de hora */
pthread_mutex_lock (&cerrojo);
esperando++;
/* Cada proceso que espera incrementa el contador /*
/* de procesos en espera */
pthread_cond_wait(&nueva_hora, &cerrojo); /* y se queda esperando al cambio de hora */
pthread_mutex_unlock(&cerrojo);
return;
}
a haber usado pthread_cond_broadcast, en lugar de pthread_cond_signal,
/* NOTA: se podr
o en clase. Entonces no har
a falta contador, ya que equivale
* pero no se explic
al notifyAll de Java. */

Subpregunta 4.2 (2 puntos)


Escriba un monitor java que represente un reloj con la misma funcionalidad del de la pregunta anterior
pero con un metodo adicional para esperar a que pase el n-simo cambio de hora. El esquema de la clase es
el siguiente:
public class Tiempo { public int hora; public int minuto; public int segundo;
}
public class Reloj extends Tiempo{
...
... void otro_segundo() {
// Sucede un nuevo segundo
...
... Tiempo lee()
// Lee el reloj
...
... void espera_hora()
// Espera el siguiente cambio de hora
...
... void espera_horas(int n)
// Espera el n-simo cambio de hora
...
}

public class Reloj extends Tiempo{


public synchronized void otro_segundo() { // Sucede un nuevo segundo
segundo = (segundo + 1) % 60;
if (segundo == 0) {
minuto = (minuto + 1) % 60; if
(minuto == 0) {
hora = (hora + 1) % 24;
notifyAll();
}
}
}
public synchronized Tiempo lee() {

// Lee el reloj

Tiempo elTiempo = new Tiempo();


elTiempo.hora = hora;
elTiempo.minuto = minuto;
elTiempo.segundo = segundo;
return elTiempo;
}
public synchronized void espera_hora()// Espera el siguiente cambio de hora
throws InterruptedException{
wait();
}
public synchronized void espera_horas(int n)// Espera el n-simo cambio de hora throws
InterruptedException{
while (n>0) {
wait();
n--;
}
}
}

Sept 2008 Pregunta 4 (3 puntos)


Se quiere desarrollar un monitor (GestorEvento) para gestionar un evento. Se considera que un evento es un objeto que puede
estar en dos estados: activo e inactivo. Una hebra controladora se encarga de notificar cuando el evento cambia de valor. Las
mero determinado de transiciones
u
hebras cuya operacio
n depende del evento pueden solicitar bloquearse hasta que se produzca un nu
o
del evento de activo a inactivo. Se debe asegurar que no se notifica un cambio de estado del evento hasta que las hebras bloqueadas
hayan actualizado su estado, de acuerdo al cambio anterior.
Se proporciona el esquema del monitor:
public class GestorEvento
{
. . . .
. . . . cambiarAActivo() . . .
. . . . cambiarAInactivo() . . .
. . . . esperar(int NumOcurrencias) . . .
}
El comportamiento de los metodos debe ser el siguiente:
El metodo cambiarAActivo() lo invoca la hebra controladora para notificar un cambio en el estado del evento de inactivo a
activo.
El metodo cambiarAInactivo() lo invoca la hebra controladora para notificar un cambio en el estado del evento de
activo a inactivo.
El metodo esperar() lo invoca una hebra para esperar hasta que ocurran un determinado nu
mero de transiciones
(NumOcurrencias) del estado del evento de activo a inactivo.
public class gestorEvento {
private estadoEvento estado = estadoEvento.Inactivo;
private boolean cambioProcesado = true;
private int
nHebras = 0;
private int
nHebrasPendientes = 0;
public synchronized void cambiarAActivo()
throws InterruptedException {
while (!cambioProcesado) wait();
if (estado == estadoEvento.Inactivo && nHebras > 0){
cambioProcesado = false;
nHebrasPendientes = nHebras;
estado = estadoEvento.Activo;
notifyAll();
}
}
}
public synchronized void cambiarInactivo()
throws InterruptedException {
while (!cambioProcesado) wait();
estado = estadoEvento.Inactivo;
}
public synchronized void esperar(int numOcurrencias)
throws InterruptedException {
while (!cambioProcesado) wait();
nHebras++;
while (numOcurrencias > 0) {
wait();
if (!cambioProcesado){
numOcurrencias;
nHebrasPendientes ;
if (nHebrasPendientes == 0){
cambioProcesado = true;
notifyAll();
}
}
}
nHebras ;
}

Jun 2009

Pregunta 5 (2,5 puntos)

Se quiere desarrollar un monitor para ordenar el acceso de un conjunto de hebras a un recurso com- partido. El enfoque se
basa en un nmero de orden que debe solicitar la hebra antes de intentar acceder al recurso. El monitor debe asegurar que
siempre acceder la hebra con el menor nmero. Este es el mismo esquema que se emplea en los mercados, para determinar el
siguiente cliente al que se debe atender entre los que esperan.
El monitor proporciona tres operaciones:
long solictarTurno(): Mediante esta operacin, una hebra obtiente un nmero de orden para acceder al recurso.
void accederRecurso(long turno): La hebra intenta acceder al recurso, proporcionando al monitor el nmero
de orden obtenido previamente. La hebra quedar bloqueada en el monitor hasta que sea su turno, es decir, cuando su
nmero sea el menor entre las que esperan para acceder al recurso.
void liberarRecurso(): Mediante este mtodo, la hebra indica al monitor que ha terminado de utilizar el recurso y
que, por tanto, ste queda libre.
El esquema de los hebras que acceden al menciondo recurso es:
MonitorOrden UnMonitorOrden = new MonitorOrden(); long
NOrden = 0;
. . .
while (true) {
. . .
NOrden = unMonitorOrden.solicitarTurno();
. . .
unMonitorOrden.accederRecurso(NOrden);
// Uso del recurso
. . .
unMonitorOrden.liberarRecurso();
. . .
}
Notas:
solitarTurno emplea internamente un mtodo llamado nmeroSiguiente() que devuel- ve un nmero entero mayor
que el obtenido en la invocacin previa del mismo. No es necesario implementar este mtodo.
No es necesario considerar el caso de una hebra que an no ha invocado accederRecurso cuando es su turno, de acuerdo al
nmero de orden asignado.
public cl a ss n on ito rOrdenAleatorio {
p r i v a t e n u m e r o s O r d e n n u me r o Or d e n = new n u m e r o s O r d e n ( ) ;
p r i v a t e long n um ero A ctu a l = 0 ;
p r i v a t e l o n g en tero Mu yGra nd e = 1 0 0 0 0 0 0 0 ;
p r i v a t e l o n g n u me r o M in i mo = e n t e r o M u y G r a n d e ;
p r i v a t e i n t nu mero He bra s = 0 ;
p r i v a t e b o o l e a n recu rso Oc u p a d o = f a l s e ;
p r i v a t e i n t n u me r o He b r a s Op = 0 ;
p u b l i c s y n c h r o n i z e d lo ng s o l i c i t a r T u r n o ( ) {
r e t u r n n u me r o Ord e n . n u m e r o S i g u i e n t e ( ) ;
}
p u b l i c s y n c h r o n i z e d v o i d a c c e d e r R e c u r s o ( l o n g n u mero Ord en , i n t i d ) t h r o w s I n t e r r u p t e d E x c e p t i o n {
n u m e r o H e b r a s ++;
w h i l e ( ! ( ! r e c u r s o O c u p a d o && ( n u m e r o Ord e n == n u m e r o A c t u a l | | n u m e r o H e b r a s w a i t ( ) ;))){
i f ( n u me r o Ord e n < n u me r o M in i mo ) {
n u me r o Min i mo = n u me r o Or d e n ;
}
n u me r o He b r a s Op ++;
i f ( n u me r o He b r a s Op == n u m e r o H e b r a s ) {
n u m e r o A c t u a l = n u me r o M in i mo ;
notifyAll ();
}
e l s e wait ( ) ;
}
n u me r o M in imo
= enteroMuyGra nde ;
n u me r o He b r a s Op = 0 ;
r e c u r s o O c u p a d o = t r u e ; n u m e r o H e b r a s ;
}
public synchronized void l i b e r a r R e c u r s o ( ) {
recu rso Ocu p a d o = f a l s e ;
notifyAll ();
}
}

10

Sept 2009

Pregunta 4 (3 puntos)

Se tiene un sistema compuesto por un conjunto inicial de N hebras, que con un periodo constante leen datos de entrada
desde sensores diferentes y realizan un determinado clculo con ellos. Se quiere desarrollar un monitor que permita identificar a las
hebras que funcionen incorrectamente. La deteccin se basa en el clculo de un valor promedio a partir de todos los resultados
obtenidos por las hebras activas en la misma iteracin. Aquellas hebras cuyo valor calculado difiera en ms del 10 % del valor
promedio, se consideran errneas y deben terminar su ejecucin. Hay que tener en cuenta que cuando una hebra es errnea,
nunca volver a invocar al monitor.
Se pide desarrollar el monitor validarResultado para sincronizar la ejecucin de las hebras. ste pro- porciona el
mtodo esResultadoErroneo, que determina si el resultado es errneo y, por tanto, la hebra correspondiente tambin lo
es. Este mtodo debe asegurar que el valor promedio se calcule con los valores propor- cionados por las hebras activas (aquellas que
no son incorrectas) en la misma iteracin.
El esquema del monitor pedido es el siguiente:
public c l a s s va l i d a r R e s ul t a do {
i n t n H e b r as I n i c i al e s = 20;
validarResultado.java
i n t [ ] v a l o r e s = new i n t [ n H e b r a s I n i c i a l e s ] ;
private i n t calcularP romedio ( i n t [ ] los Valores ) {
}
. . . . boolean e s R e s u l t a d o E r r o n e o ( i n t v a l o r ) . . .

A continuacin se proporciona un esquema de las hebras, con objeto de comprender ms fcilmente su comportamiento. No es
necesario proporcionar el cdigo de las mismas.
hebraCalculadora.java
1
2
3
4

5
6
7
8
9
10
11

p u b l i c c l a s s h e b r a C a l c u l a d o r a e x t e n d s Thread {
. . .
public void run ( ) {
i n t valor = 0;
. . .
do {
esperarInicioSiguienteIteracion ()
datos = leerDatos ()
valor = ca lc ul arVa lo r ( datos ) ; }
while ( ! m o n i t o r V a l i d a r R es u l t a d o . e s R e s u l t a d o E r r o n eo ( v al o r ) )
}}

public class validarResultado {


p r i v a t e i n t n H e b r a s I n i c i a l e s = 3;
p r i v a t e i n t nHebr asActivas = n H e b r a s I n i c i a l e s ;
i n t [ ] v a l o r e s = new i n t [ n H e b r a s I n i c i a l e s ] ;
i n t v a l o r e s D i s p o n i b l e s = 0;
boolean es t n T o d a s = f a l s e ;
i n t valorPromedio = 0;
p r i v a t e i n t calcular Pr omedio ( i n t [] l os V a lo r es ) {
i n t elPromedio = l os V a lo r es [ 0] ;
i f ( elPromedio > l o s V a l o r e s [ 1] ) elPromedio = l o s V a l o r es [ 1 ] ;
r et ur n elPromedio ;
}
p u b l i c s y n c h r o n i z e d b oo lean e s R e s u l t a d o E r r o n e o ( i n t id , i n t v a l o r ) th rows I n t e r r u p t e d E x c e p t i o n {
boolean esErroneo = f a l s e ;
while ( es tnTodas ) wait ( ) ;
valores [ valor es Dis ponibles ] = valor ;
v a l o r e s D i s p o n i b l e s ++;
i f ( v a l o r e s D i s p o n i b l e s == n H e b r a s A c t i v a s ) {
valorPromedio = ca lcular Pr o med io ( v a l o r es ) ;
es tnTodas = t r u e ;
notifyAll ();
}
else {
while ( ! es tnTodas ) wait ( ) ;
}
v a l o r e s D i s p o n i b l e s ;
i f ( v a l o r > 1 . 1 v a l o r P r o m e d i o | | v a l o r < 0 . 9 v a l o r P r o m e d i o ){
esErroneo = t r u e ;
n H e b r a s A c t i v a s ;
}
i f ( v a l o r e s D i s p o n i b l e s == 0 ) {
es tnTodas = f a l s e ;
notifyAll ();
}
r et u r n esErroneo ;
}}

11

Jun 2010

Pregunta 3 (3 puntos)

Un sistema est compuesto por un conjunto de hebras que toman medidas de una magnitud fsica. Estas medi- das solo son
tiles durante un intervalo de tiempo que llamaremos ciclo, durante el cul cada hebra toma una sola medida. Otra hebra, llamada
gestora, determina cuando empieza y termina un ciclo. La hebra que durante un ciclo haya tomado el valor ms alto, deber hacer
una serie de operaciones adicionales (fuera del monitor).
Se pide desarrollar un monitor que seleccione la hebra con la medida mayor durante un ciclo. Cuando las
hebras leen un valor, invocan el mtodo notificarValor. El monitor debe proporcionar los siguientes mtodos:
inicioCiclo: La hebra gestora invoca este mtodo para indicar el inicio de un ciclo. A partir del momento en que se
notifica el ciclo, se debe considerar los valores de las hebras, para la seleccin del mayor.
Si cuando se notifica el inicio de un ciclo hay hebras en el monitor del ciclo anterior, se debe bloquear a la hebra gestora
hasta que abandonen del monitor.
finCiclo: La hebra gestora invoca este mtodo para indicar el final de un ciclo. Como consecuencia, hay que desbloquear
a la hebra que haya leido el valor mayor.
notificarValor: Una hebra invoca este mtodo para notificar el valor leido. Si no ha comenzado un ciclo, el valor
proporcionado no se considerar. Si ha comenzado un ciclo, se debe comprobar si el valor proporcionado es el mayor. Si
es as, se debe bloquear a la hebra hasta el final del ciclo y permitir continuar a la hebra que tuviera el mayor valor con
anterioridad. Este mtodo slo devuelve true a la hebra que haya proporcionado el valor ms alto en un ciclo. En el resto
de los casos, se debe retornar el valor false, bien por que se est fuera de un ciclo o por que la hebra no haya leido el
valor mayor.
Supngase que las hebras en un ciclo nunca leen exactamente el mismo valor. El
monitor a desarrollar debe seguir el siguiente esquema:
Public class gestorHebras {
. . . .
. . . . void inicioCiclo () . . .
. . . . void finCiclo() . . .
. . . . boolean notificarValor (int elValor) . . .
}
public c l as s gestorHebras {
p r i v a t e boolean c i c l o A c t i v o = f a l s e ;
p r i v a t e boolean hayEsperando = f a l s e ;
p r i v a t e i n t v a l o r M i n i m o = I n t e g e r . MIN_VALUE;
p ub l i c s ynchroniz ed vo id i n i c i o C i c l o ( ) throws I n t e r r u p t e d E x c e p t i o n {
if (! cicloActivo ) {
w h i l e ( hayEsperando ) w a i t ( ) ;
}
}
public synchronized

v o i d f i n C i c l o ( ){

i f ( cicloActivo ){
cicloActivo = false ;
notifyAll ();
}
}
p u b l i c s y n c hr on i z e d boolean n o t i f i c a r V a l o r ( i n t e l V al o r , i n t i d C l i e n t e ) throws I n t e r r u p t e d E x c e p t i o n {
i f ( ! c i cl o A c t i v o ) return f a l s e ;
hayEsperando = t r u e ;
i f ( v a l o r M i n i m o <= e l V a l o r ) v a l o r M i n i m o = e l V a l o r ;
n o t i f y A l l ( ) ; / / V e r s i s e p u e d e d e j a r e n un n o t i f y a s e c a s
w h i l e ( e l V a l o r >= v a l o r M i n i m o && c i c l o A c t i v o ) w a i t ( ) ;
i f ( e l V a l o r == v a l o r M i n i m o ) {
hayEsperando = f a l s e ;
notifyAll ();
v a l o r M i n i m o = I n t e g e r . MIN_VALUE ;
return true ;
}
e l se return f als e ;
}
}

12

Sept 2010 Pregunta 4 (3 puntos)


En una mina se quiere controlar el nivel de agua que se acumula mediante una bomba de achique. Una he- bra accede a
un sensor para conocer cul es el nivel de agua en la mina. Si es mayor que un valor umbral (UmbralSuperiorAgua) se
debe activar el motor. Cuando el nivel sea menor que un valor umbral (UmbralInferiorAgua), se deber detener.
El estado del motor tambin depende del volumen de gas metano que se acumula en la mina. Una hebra accede a un
sensor para conocer el volumen de metano de la mina. Si el volumen de gas es mayor que un umbral (UmbralSuperiorMetano), no
se podr tener el motor funcionado, independientemente del nivel de agua, ya que sera peligroso.
El motor lo controla una hebra, a la que se le indica si hay que enceder o apagar el mismo.
Se debe desarrollar el monitor GestorMina de acuerdo con el comportamiento descrito previamente y con los siguientes
mtodos:
... void notificarNivelMetano(int nivelMetano)...: La hebra correspondiente notifica al monitor la ltima lectura del
volumen de mentano en la mina.
... void notificarNivelAgua(int nivelAgua)...: La hebra correspondiente notifica al monitor la ltima lectura del nivel de
agua.
... EstadoMotor obtenerAccinMotor() ...: La hebra correspondiente accede al monitor para saber cul debe ser el estado
del motor. Esta hebra se debe bloquear en el monitor hasta que el estado del motor deba cambiar.
Nota: Declaracin de EstadoMotor:
public enum EstadoMotor {motorParado, motorFuncionando}
2

p u b l i c c l a s s GestorMina {

p r i v a t e s t a t i c f i n a l i n t umbralSuperiorAgua
= 10;
p r i v a t e s t a t i c fi n a l i n t umbralInferiorAgua
= 3;
p r i v a t e s t a t i c f i n a l i n t umbralSuperior Me tano = 100;

*
*
*
7
9
14
15
16
17

//
//

18
19

private
private
private
private
private
private

int
nivelAgua
int
nivelMetano
int
nivelPrevioMetano
boolean nivelElevadoMetano
EstadoMotor estadoMotor
EstadoMotor n u e v o E s t a d o Mo t o r

=
=
=
=
=
=

0;
0;
0;
false ;
Es tadoMotor . motorP arado ;
EstadoMotor . motor Parado ;

16
17

p r i v a t e EstadoMotor compr obar AccinMotor ( Es tadoMotor e s t a d o Mo t o r )


{
EstadoMotor nuev oE stadoMotor = es tadoMotor ;

5
6
7
21

s wit c h ( estadoMotor ) {
cas e motorParado :
i f ( n i v e l A g u a > u m b r a l S u p e r i o r A g u a && n i v e l M e t a n o <= u m b r a l S u p e r i o r M e t a
no ) {
notify ();
n u e v o E s t ad o Mo t o r = EstadoMotor . motorFuncionando ;
}

12
13
14
15
16
17
28

case motorFuncionando : {
i f ( n i v e lA g ua < u mb r al In fe r io r A g ua | | n iv el Me t an o > umbralSuperiorMetan
o) {
notify ();
n u e v o E s t a d o Mo t o r = Es tadoMotor . motorPar ado ;
}
}
}
r et ur n nuevoEstadoMotor ;

27
28
29
30
31
32
33
34

35
38

public
{

36
37

synchronized void n o t i f i c a r N i v e l A gu a ( i n t nivelAgua )


t hi s . nivelAgua = nivelAgua ;
n u e v o E s t a d o Mo t o r = compr obar AccinMotor ( nu e v o E s t a d o Mo t o r ) ;

38
39
43

44
45

public
{

43
44

synchronized void notific ar Niv elMe ta no ( i n t nivelMetano )


t h i s . nivelMetano = nivelMetano ;
n u e v o E s t a d o Mo t o r = compr obar AccinMotor ( nu e v o E s t a d o Mo t o r ) ;

45
46

47
51

public
{

48
49

w h i l e ( e s t a d o M o t o r == n u e v o E s t a d o M o t o r ) w a i t ( ) ;
estadoMotor = nue voEs tadoMotor ;
r et ur n estadoMotor ;

50
51
52

53
54

s y n c h r o n iz e d EstadoMotor o b t e n e r A c c i n M ot o r ( ) throws I n t e r r u p t e d E x c e p t i o n

13

Jun 2011

Pregunta 3 (3 puntos)

Se quiere desarrollar un monitor para ordenar el acceso de un conjunto de hebras a un recurso compartido. El
enfoque se basa en un nmero de orden que debe solicitar la hebra antes de intentar acceder al recurso. El monitor
mantiene una variable (siguienteHebra) con el valor del nmero de la siguiente hebra que debe entrar. ste es
un esquema similar al que se emplea en los mercados, para determinar el siguiente cliente al que se debe atender.
Cuando la hebra quiere acceder al recurso, llama al mtodo pertinente, en el que se quedar bloqueada hasta
que llegue su turno. En estas condiciones, es posible que ocurra un bloqueo indefinido si una de las hebras que ha
solicitado un nmero nunca intenta acceder al recurso.
Para evitar esta situacin, se emplea un temporizador, que se activa cuando: a) finaliza una operacin y hay
hebras bloqueadas; b) expira el temporizador y hay hebras bloqueadas; c) una hebra entra en el monitor, no es su
turno y no hay otras hebras esperando.
El temporizador se cancela cuando la hebra a la que corresponde el turno accede al recurso.
Si el temporizador expira, se incrementa el valor de la variable de turno del monitor (siguienteHebra). La
hebra a la que se le ha pasado el turno no podr acceder al recurso con ese nmero.
Se pide desarrollar el monitor GestorTurno (se muestra un esquema del mismo) de forma que gestione el
turno de acceso de un conjunto de hebras a un recurso compartido, segn la descripcin previa. El monitor debe
proporcionar los siguientes mtodos:
long solicitarTurno(): Mediante esta operacin, una hebra obtiente un nmero de orden para acceder al recurso.
void accederRecurso(long turno): La hebra intenta acceder al recurso, proporcionando al monitor el nmero de orden obtenido previamente. La hebra quedar bloqueada en el monitor hasta que sea su
turno, es decir, cuando su nmero coincida con el nmero de turno que mantiene el monitor.
void liberarRecurso(): Mediante este mtodo, la hebra indica al monitor que ha terminado de utilizar el recurso y que, por tanto, ste queda libre.
void notificarExpiracion(): El objeto de la clase Temporizador llama a este mtodo automaticamente cuando expira el temporizador que hemos armado.
p u b l i c c l a s s GestorTurno {
p r i v a t e T e m p o r i z a d o r temp
= new T e m p o r i z a d o r ( t h i s ) ;
p r i v a t e long valorArmadoTemporizador
= 5000; / / Milisegundos
p r i v a t e long ul t i maH ebr a
= 0;
p r i v a t e long s i g u i e n t e H e b r a
= 0;
. . . . .
. . . . long s o l i c i t a r T u r n o ( ) {
u l t i m a H e b r a ++; retu rn u l t i m a H e b r a ; }
. . . . v o i d a c c e d e r R e c u r s o ( i n t mi T urno ) { . . . . }
. . . . void l i b e r a r R e c u r s o ( ) { . . . . }
. . . . void n o t i f i c a r E x p i r a c i o n ( ) { . . . . }
}
Se supone que las hebras nunca llaman a accederRecurso sin haber llamado antes a solicitarTurno.
Adems, siempre realizan la llamada con el turno que han recibido. Sin embargo, puede haber hebras que soliciten
turno y nunca accedan al recurso.
A continuacin se muestra un esquema de la clase Temporizador, nicamente para saber la interfaz de la
misma. No hay que desarrollarla.
public
public
public
public

c l a s s Temporizador {
Temporizador ( GestorTurno o b j e t o ) { . . . . }
void a rma rT empori za dor ( long m i l l i s ) { . . . . }
void c a n c e l a r T e m p o r i z a d o r ( ) { . . . . }

14

p u b l i c c l a s s GestorTurno {
2

private
private
private
private
private

*
*
*
*
*

i n t ultimaHebra
= 0;
i n t s i g u i e n t e H e b r a = 0;
T e m p o r i z a d o r te mp = new T e m p o r i z a d o r ( t h i s ) ;
i n t nHebrasEspera = 0;
long v alorA r madoTempor iz ador = 5000;

public synchronized i n t s o l i ci t a r Tu r n o ( ) {
u l t i m a H e b r a ++;
r etur n ultimaHebra ;
}

20
21
22
23
13

p u b l i c s y n c h r o n i z e d v o i d a c c e d e r R e c u r s o ( i n t miTur no )
throws I n t e r r u p t e d E x c e p t i o n {

8
9
16

i f ( miTur no < s i g u i e n t e H e b r a ) { r e t u r n ; }
i f ( miTur no > s i g u i e n t e H e b r a && n H e b r a s E s p e r a == 0 ) {
te mp . a r m a r T e m p o r i z a d o r ( v a l o r A r m a d o T e m p o r i z a d o r ) ;
}
n H e b r a s E s p e r a ++;
w h i l e ( miTur no > s i g u i e n t e H e b r a ) w a i t ( ) ;
te mp . c a n c e l a r T e m p o r i z a d o r ( ) ;

18
36
37
38
39
40
41

42
31

public synchronized void liberarRecurs o () {


n H e b r a s E s p e r a ;
s i g u i e n t e H e b r a ++;
i f ( nHebrasEspera > 0) {
te mp . a r m a r T e m p o r i z a d o r ( v a l o r A r m a d o T e m p o r i z a d o r ) ;
}
notifyAll ();
}

40
41
42
43
44
45
46
47
40

public synchronized void n o t i f i c a rE x p i r a ci o n ( ) {


s i g u i e n t e H e b r a ++;
i f ( nHebrasEspera > 0) {
te mp . a r m a r T e m p o r i z a d o r ( v a l o r A r m a d o T e m p o r i z a d o r ) ;
}
notifyAll ();
}

48
49
50
51
52
53
54
48
49

15

Sept 2011 Pregunta 5 (3 puntos)


Se quiere desarrollar un sistema de gestin de subastas. Una sesin de subasta est compuesta por un conjunto de lotes. Los
lotes se identifican mediante un nmero natural y se les asignan nmeros consecutivos. Al iniciar la subasta de un lote, se fija un
precio. La subasta del lote se completa cuando se produce una puja por esta cantidad o cuando el gestor decide finalizarla. La
oferta con mayor valor obtendr el lote.
Para realizar este sistema se quiere desarrollar un monitor (GestorSubasta) que sincronice a la hebra gestora y a un
conjunto de hebras compradoras. La hebra gestora inicia y puede finalizar la subasta de un lote. Las hebras compradoras pueden
realizar ofertas por un lote. El monitor proporciona las siguientes operaciones:
... void IniciarSubastaLote(int precio): Se inicia el procedimiento de subasta de un lote. Cuan- do se
inicia una nueva subasta, el monitor incrementa automaticamente el nmero del lote.
... void FinalizarSubastaLote(): Se termina la subasta de un lote. A partir de este momento, no se
admitirn nuevas pujas para este lote. El lote se asigna a la hebra compradora que haya ofrecido el valor mayor.
... boolean PujarLote(int lote, int oferta): Este mtodo lo invocan las hebras compradoras para
pujar por un artculo. Las ofertas sobre lotes cuya subasta haya finalizado, se desechan de inmediato y se retorna el valor
false. Si el nmero de lote en la invocacin es mayor que el lote actual, se debe bloquear a la hebra hasta que se inicie la
subasta del lote indicado en la invocacin.
El monitor mantiene bloqueada a la primera hebra que haya realizado la mayor oferta al lote actual. Las hebras que hayan
realizado una oferta menor o igual a la mxima, deben salir del monitor y retornar el valor false. Cuando se finaliza la
subasta, la hebra que haya realizado en primer lugar la puja ms alta se desbloquea y recibe el valor true.

*
*
*
*
*

public c las s GestorSubasta {


i n t nLote
= 0;
int precioLote
= 0;
int ofertaLote
= 0;
boolean s u b a s t aA c t i v a = f a l s e ;

public synchronized void in i c i ar S uba s ta Lo t e ( i n t pr ecio ) {


if (! subastaActiva ) {
subastaActiva = true ;
precioLote
= precio ;
ofertaLote
= 0;
n L o t e ++;
notifyAll ();
}
}

24
25
26
27
28
29
30
31
32
16

public synchronized void f i n a l i za r S u b a s t a L o t e () {


if ( subastaActiva ) {
subastaActiva = false ;
notifyAll ();
}
}

10
11
12
13
14
15
23

p ub l i c s ynchr onized boolean pujar Lote ( i n t l ot e , i n t o f e r t a )


throws I n t e r r u p t e d E x c e p t i o n {
/ / B l o q u e o de o f e r t a s de l o t e s c u y a s u b a s t a no ha c ome nz ado
while ( l o t e > nLote ) wait ( ) ;
/ / F i n de e j e c u c i on de h e b r a s c on o f e r t a s a l o t e s c u y a
/ / s u b a s t a y a ha f i n a l i z a d o
i f ( ( l o t e < n L o t e ) | | ( l o t e == n L o t e && ! s u b a s t a A c t i v a ) ) {
return f al se ;
}
if ( oferta > ofertaLote ) {
/ / E s t a o f e r t a e s l a m ax ima
ofertaLote = oferta ;
/ / S i l a o f e r t a e s mayor o i g u a l que e l p r e c i o , l a s u b a s t a
/ / d e l l o t e ha f i n a l i z a d o
i f ( p r e c i o L o t e <= o f e r t a ) {
subastaActiva = false ;
notifyAll();
return true;
}
w h i l e ( ( o f e r t a == o f e r t a L o t e ) && ( s u b a s t a A c t i v a ) ) { w a i t ( ) ; }
i f ( o f e r t a == o f e r t a L o t e ) {
/ / La s u b a s t a d e l l o t e ha f i n a l i z a d o y e s l a mayor o f e r t a
return true;
} else {
/ / Se ha r e c i b i d o una o f e r t a mayor
return false;
}
} else {
/ / La o f e r t a e s menor que l a m ax ima
return false;
}
}

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

16

Jun 2012

Pregunta 4 (3 puntos)

Los aviones al despegar generan turbulencias, por lo que entre dos despegues consecutivos tiene que transcurrir un
intervalo de tiempo mnimo. Se tiene un aeropuerto del que despegan aviones normales y aviones VIP. Los aviones VIP tienen
preferencia, pero no pueden despegar dos aviones VIP consecutivamente mientras haya aviones de otro tipo esperando. El
intervalo de despegue entre dos aviones es de dos minutos.
Se pide desarrollar un monitor (GestorDespegue) que gestione el despegue de los aviones segn la especifi- cacin previa.
Un avin que solicita despegar debe permanecer bloqueado en el monitor hasta que tenga permiso. El monitor debe
proporcionar los siguientes mtodos:
void despegarAvion(): Este mtodo lo invoca un avin cuando quiere despegar.
void despegarAvionVIP(): Este mtodo lo invoca un avin VIP cuando quiere despegar.
void finTemporizador(): Este mtodo se invoca para indicar que el intervalo de tiempo de espera entre dos aviones ha
concluido.
Para gestionar este intervalo de tiempo, se dispone de una clase Temporizador, con el interfaz que se mues- tra a
continuacin. El mtodo armarTemporizador arma un temporizador. Cuando expira, se invoca al mtodo finTemporizador del
objeto de la clase GestorDespegue que se pasa en el constructor. No es necesario desa- rrollar esta clase.
public class Temporizador {
public Temporizador(GestorDespegue unGestor) {. . .}
public void armarTemporizador(int numeroMinutos) {. . .}
}
p ub l i c c l a s s GestorDespegue {
private
private
private
private
private
private

boolean pistaOcupada = t r u e ;
int
nVIPEsperando
= 0;
int
nEsperando
= 0;
f i n a l i n t tiempoAvion = 3;
boolean
anteriorVIP = f alse ;
T e m p o r i z a d o r u n T e m p o r i z a d o r = new T e m p o r i z a d o r ( t h i s ) ;

p u b l i c s y nc hr oni z e d v oi d despegarAvion ( ) throws I n t e r r u p t e d E x c e p t i o n {


n E s p e r a n d o ++;
w h i l e ( p i s t a O c u p a d a | | ( n V I P E s p e r a n d o > 0 && ! a n t e r i o r V I P ) ) w a i t ( ) ;
n E s p e r a n d o ;
anteriorVIP = fal se ;
unTemporizador . armarTemporizador ( tiempoAvion ) ;
pistaOcupada = t r ue ;
}
p u b l i c s ynchr o niz e d voi d despegarAvionVIP ( ) throws I n t e r r u p t e d E x c e p t i o n {
n V I P E s p e r a n d o++;
w h i l e ( p i s t a O c u p a d a | | ( n E s p e r a n d o > 0 && a n t e r i o r V I P ) ) w a i t ( ) ;
nV IP E s pe r ando ;
anteriorVIP = true ;
unTemporizador . armarTemporizador ( tiempoAvion ) ;
pistaOcupada = t r ue ;
}
p ub l i c s ynchr onized void finTempor iz ador ( ) throws I n t e r r u p t e d E x c e p t i o n {
pistaOcupada = f a l s e ;
notifyAll ();
}
}

17

Sept 2012

Pregunta 6 (3 puntos)

Se tiene un edificio con 20 plantas y cuatro ascensores. En cada planta hay un pulsador para solictar un ascensor. Cuando
una persona solicita un ascensor desde una planta, el ascensor libre ms cercano ser el que deba acudir a la llamada. Si todos
los ascensores estn ocupados, la peticin queda bloqueada hasta que quede uno libre. Los ascensores se identifican con valores
enteros en el rango 0..3.
Se pide desarrollar un monitor (GestorAscensores) que gestione las solicitudes de un ascensor desde los
pulsadores de las plantas y que proporcione las siguientes operaciones.
void solicitarAcensor(int piso): las personas invocan este mtodo para solicitar un ascensor desde la planta identificada
por el parmetro. La hebra llamante queda bloqueada hasta que haya un ascensor libre que satisfaga su peticin.
int notificarAscensorLibre(int ascensor, int piso): Un ascensor invoca este mtodo cuan- do est libre. Los parmetros
indican el identificador del ascensor y el piso en el que se encuentra. La hebra correspondiente queda bloqueada en el
monitor hasta que sea solicitado. Este mtodo retorna el entero que identifica la planta a la que el ascensor debe
desplazarse.
Notas:
No es necesario tratar las peticiones en el orden en el que llegan. Supngase que
nunca hay ms de una peticin desde la misma planta.
Ntese que el monitor solicitado gestiona las peticiones de ascensores desde el exterior de los mismos y no trata las
rdenes a los ascensores desde los botones interiores, que seran gestionadas por otros componentes de software

public c lass GestorAscensores {


private
private
private
private
private
private
private

s t a t i c f i n a l i n t nAscensores = 2;
b o o l e a n a s c e n s o r L i b r e [ ] = new b o o l e a n [ ] { t r u e , t r u e } ;
i n t p o s i c i o n A s c e n s o r [ ] = new i n t [ ] { 0 , 0 , 0 , 0 } ;
int nAscensoresLibres
= 0;
i n t a s c e n s o r M a s C e r c a n o = 1;
i n t idPlant a = 0;
i n t nPersona = 0;

p u b l i c sy nc hro ni ze d v o i d s o l i c i t a r A s c e n s o r ( i n t i d Pl a nt aPe r s o n a , i n t nPersona )


throws I n t e r r u p t e d E x c e p t i o n {
i n t di st a n ci a M i n i m a = 9999;
int distancia ;
w h i l e ( n A s c e n s o r e s L i b r e s == 0 ) w a i t ( ) ;
a s c e n s o r M a s C e r c a n o = 1;
f o r ( i n t i = 0 ; i < n A s c e n s o r e s ; i ++ ) {
i f ( ascensorLibre [ i ]) {
d i s t a n c i a = Math . a b s ( i d P l a n t a P e r s o n a p o s i c i o n A s c e n s o r [ i ] ) ;
i f ( d ista nc ia < distanciaMinima ) {
ascensorMasCercano = i ;
distanciaMinima
= distancia ;
}
}
}
t h i s . idPlanta = idPlantaPersona ;
t h i s . nPersona = nPersona ;
n A s c e n s o r e s L i b r e s ; / / Para e v i t a r q u e s i una p e r s o n a s e a d e l a n t a a l a s c e n s o r
/ / p i e n s e que hay a s c e n s o r e s l i b r e s
notifyAll ();
}
public synchronized i n t no t i f i c a r A s c e ns o r L i b r e ( i n t idAscensor , i n t idPlantaAscensor )
throws I n t e r r u p t e d E x c e p t i o n {
notifyAll ();
ascensorLibre [ idAscensor ] = true ;
n A s c e n s o r e s L i b r e s ++;
posicionAscens or [ idAscensor ] = idPlantaAscensor ;
w h i l e ( a s c e n s o r M a s C e r c a n o != i d A s c e n s o r ) w a i t ( ) ;
a s c e n s o r M a s C e r c a n o = 1;
ascensorLibre [ idAscensor ] = f a l se ;
return t h i s . idPlanta ;
}
}

18

Dic 2012

Pregunta 5 (3 puntos)

Un sistema est compuesto por un conjunto de hebras cliente, que tienen trabajos pendientes, y hebras trabaja- doras, que llevan a cabo
estos trabajos. Las hebras trabajadoras tienen asignada una prioridad, que est asociada a la rapidez con la que completan sus encargos. Los
trabajos son objetos de la clase Actividad.
Se pide desarrollar el monitor GestorActividades, con los siguientes mtodos:
... Actividad solicitarActividad(int prioridad): Este mtodo lo ejecutan las hebras traba- jadoras que quieren solicitar una actividad. Si no hay
actividades pendientes, deben esperar hasta que las haya. En el monitor, no deben esperar ms de tres hebras. A las hebras que invoquen este
mtodo cuando se cumpla esta condicin se les debe retornar el valor null. La hebra trabajadora a la que se le asigna una actividad debe ser la
ms prioritaria entre las que estn esperando.
... void proporcionarActividad(Actividad actividad): Este mtodo lo invocan las hebras clien- te cuando tienen una actividad pendiente. Si no hay
hebras trabajadoras en el monitor, la hebra cliente debe esperar hasta que llegue alguna y se le asigne una actividad.
No es necesario implementar la clase Actividad.
Supngase que en la clase GestorActividades estn definidos los siguientes mtodos privados:
private int incluirHebra(int prioridad, int[] colaHebras): Este mtodo incluye un valor entero en una posicin vaca de un array de
enteros y retorna la posicin en la que lo ha aadido.
private int obtenerPosicionMax(int[] colaHebras): Retorna la posicin del valor mayor alma- cenado en el array.
p u b l i c c l a s s GestorHebras {
p r i v a t e s t a t i c f i n a l i n t POSICION_VACIA = 1;
p r i v a t e i n t nMaxHebras ;
p r i v a t e i n t [ ] colaHebras ;
p r i v a t e i n t nHebrasTrabajadoras = 0;
p r i v a t e i n t p o s i c i o n M a s P r i o r i t a r i a = POSICION_VACIA ;
p r i v a t e i n t nHebrasCliente = 0;
pri vat e Actividad a ct i vi da dSi g ui e nt e = null ;
p u b l i c G e s t o r H e b r a s ( i n t nMaxHebras ) {
t h i s . nMaxHebras = nMaxHebras ;
c o l a H e b r a s = new i n t [ nMaxHebras ] ;
f o r ( i n t i = 0 ; i < nMaxHebras ; i ++) {
c o l a H e b r a s [ i ] = POSICION_VACIA ;
}
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p r i v a t e i n t obt en erP osicionMa x ( i n t [ ] colaHebras ) {
i n t posicion = 0;
i n t m a x P r i o r i d a d = 1;
f o r ( i n t i = 0 ; i < nMaxHebras ; i ++) {
i f ( colaHebras [ i ] > maxPri orida d ) {
posicion = i ;
maxPrioridad = colaHebras [ i ] ;
}
}
return posi ci on ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p r i v a t e i n t i n c l u i r H e b r a ( i n t p r io r i d ad , i n t [ ] colaHebras ) {
f o r ( i n t i = 0 ; i < nMaxHebras ; i ++) {
i f ( c o l a H e b r a s [ i ] == POSICION_VACIA ) {
colaHebra s [ i ] = p r i o r i d a d ;
return i ;
}
}
/ / Nunca s e l l e g a aqu i p u e s s o l o s e l l a m a a e s t e m e t o d o s i h a y h e b r a s en l a c o l a
return 0;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p u b l i c s y n c h r o n i z e d A c t i v i d a d s o l i c i t a r A c t i v i d a d ( i n t p r i o r i d a d ) throws I n t e r r u p t e d E x c e p t i o n {
i n t posicionEnCola ;
i f ( n H e b r a s T r a b a j a d o r a s == nMaxHebras ) r e t u r n n u l l ;
p o s i c i o n En Co l a = i n c l u i r H e b r a ( p r i o r i d a d , cola Hebras ) ;
i f ( n H e b r a s C l i e n t e > 0 && n H e b r a s T r a b a j a d o r a s == 0 ) n o t i f y A l l ( ) ;
n H e b r a s T r a b a j a d o r a s ++;
w h i l e ( p o s i c i o n M a s P r i o r i t a r i a != p o s i c i o n E n C o l a ) w a i t ( ) ;
n H e b r a s T r a b a j a d o r a s ;
c o l a H e b r a s [ p o s i c i o n M a s P r i o r i t a r i a ] = POSICION_VACIA ;
p o s i c i o n M a s P r i o r i t a r i a = POSICION_VACIA ;
n o t i f y A l l ( ) ; / / O b j e t i v o : d e s p e r t a r a heb ras c l i e n t e dormidas
return a c t i v i d a d S i g u i e n t e ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p u b l i c synch roniz ed voi d p r o p o r ci on a r T r a ba j o ( A c t i v i d a d a c t i v i d a d ) throws I n t e r r u p t e d E x c e p t i o n {
n H e b r a s C l i e n t e ++;
w h i l e ( p o s i c i o n M a s P r i o r i t a r i a != POSICION_VACIA | | n H e b r a s T r a b a j a d o r a s == 0 ) w a i t ( ) ;
p o s i c i o n M a s P r i o r i t a r i a = o b t e n e r Po s i c i o n Ma x ( col aHeb ras ) ;
n H e b r a s C l i e n t e ;
actividadSiguiente = actividad ;
n o t i f y A l l ( ) ; / / O b j e t i v o : q u e una h e b r a t r a b a j a d o r a c o j a t r a b a j o
}}

19

Jun 2013

Pregunta 4 (3 puntos)

Se quiere desarrollar un sistema de radiado de mensajes. Las hebras emisoras envan mensajes, que deben leer todas las
hebras receptoras. Un mensaje est activo desde que se acepta, hasta que lo leen todas las hebras receptoras. En un instante
dado slo puede haber un mensaje activo. Si una hebra emisora enva un mensaje y hay uno activo, deber bloquearse. Si una
hebra receptora intenta leer un mensaje y ya lo ha ledo previamente, deber esperar hasta que se seleccione otro mensaje
activo. Cuando todas las hebras receptoras hayan ledo el mensaje activo, se puede seleccionar uno nuevo entre los que quieren
enviar las hebras emisoras que estn esperando, si las hubiera.
Se pide desarrollar un monitor (GestorMensajesRadiado) que gestione la emisin y recepcin de mensajes, segn se ha
descrito. El monitor debe proporcionar los siguientes mtodos:
... void enviarMensaje(Mensaje unMensaje): Este mtodo lo invocan las hebras emisoras. Se de- bern quedar
bloqueadas si hay hebras receptoras que no hayan leido el mensaje activo previo o si se selec- ciona el mensaje activo
de otra hebra emisora. En caso contrario, ejecuta las operaciones necesarias para que las hebras receptoras puedan leer
el mensaje y sale del monitor.
... Mensaje recibirMensaje(int id): Este mtodo lo invocan las hebras receptoras. Si no hay men- saje activo o la hebra
receptora ya ha ledo el mensaje actual, deber esperar hasta que se acepte el siguiente. id es el identificador de una
hebra receptora. Considerar que hay NHebrasReceptoras. Los valores que puede tomar el identificador estn
comprendidos entre 0 y NHebrasReceptoras - 1.

pu bl ic c l a s s GestorMensajesRadiado {
public
private
private
private
private

s t a t i c f i n a l i n t NHebrasReceptoras = 10;
int
nLeidos = 0;
boolean
hayMensajeActivo = f a l s e ;
Mensaje
mensajeActivo ;
boolean [] haLeido ;

publi c GestorMensajesRadiado ( ) {
h a L e i d o = new b o o l e a n [ N H e b r a s R e c e p t o r a s ] ;
haLeidoFalso ( ) ;
}
p u b l i c s y n c h r o n i z e d v o i d e n v i a r Me n s a j e ( Mensaje unMensaje ) th rows I n t e r r u p t e d E x c e p t i o n {
while ( hayMensajeActivo ) wait ( ) ;
mensajeActivo
= unMensaje ;
hayMensajeActivo = true ;
nLeidos = 0;
haLeidoFalso ( ) ;
notifyAll ();
}
p u b l i c s y n c h r o n i ze d Mensaje r e c i b i r M e n s a j e ( i n t i d ) t hr ows I n t e r r u p t e d E x c e p t i o n {
while ( haLeido [ i d ] | | ! hayMensajeActivo ) wait ( ) ;
haLeido [ i d ] = t r ue ;
n L e i d o s ++;
i f ( n L e i d o s == N H e b r a s R e c e p t o r a s ) {
hayMensajeActivo = f a l s e ;
}
notifyAll ();
return mensajeActivo ;
}
p r i v a t e voi d haLeidoFalso ( ) {
f o r ( i n t i = 0 ; i < h a L e i d o . l e n g t h ; i ++) {
haLeido [ i ] = f a l s e ;
}
}
}

20

PRCTICAS

ndice
Prctica 1. Hebras POXIS ............................................................................................................... 3
Prctica 2. Hebras en Java ............................................................................................................. 9
Prctica 4. Comunicacin sincronizada con tuberas .................................................................. 17
Prctica 5. Recubrimientos y redirecciones ................................................................................ 23
Prctica 6. Dibujo de imgenes contenidas en ficheros con pipes ............................................. 29
Prctica 7. Productor/Consumidor con semforos ..................................................................... 33
Prctica 8. Productor/Consumidor con variables condicin y cerrojos ...................................... 47
Prctica 9. Productor/Consumidor en Java ................................................................................. 61
Prctica 11. Comunicaciones con RMI (Versin antigua) ........................................................... 79
Prctica 11. Comunicaciones con RMI (Versin moderna) ........................................................ 85

Prctica 1. Hebras POXIS


Especificacin
A continuacin se adjunta el programa hebras.c. Se nos pide que lo comparemos con
el de la prctica de procesos de ARQO, forkwait.c. Para ello debemos compilar el programa,
montarlo con la biblioteca de hebras (opcin de montaje lpthread, es decir, compilar y
montar as: gcc hebras.c lpthread o hebras), y ejecutarlo igual que se ejecut forkwait.c en
su momento, explicando su funcionamiento y resultados. Usaremos ps -m para ver las hebras
asociadas a cada proceso. Tenemos que observar que hay una hebra adicional, la hebra de control, que
no hemos programado explcitamente. Observe la definicin de _REENTRANT para asegurar que se
utilizan versiones reentrantes de las rutinas de biblioteca.
hebras.c
#define _REENTRANT
#include
#include
#include
#include
#include

/* Aseguro que slo llamo a


rutinas reentrantes */

<unistd.h>
<fcntl.h>
<stdlib.h>
<stdio.h>
<pthread.h>

#define MAXHEBRAS 100 /* Creo una constante MAXHEBRAS que vale 100,
que me servir para crear un array donde guardar parmetros de un
mximo de 100 hebras */
char** argumentos; / *El argumento de OPEN debe ser un char
puntero, esto es, una cadena de caracteres. Se pone puntero doble
para que argv = argumentos se pueda usar fuera del main */
void *escribe(void *idp) { /* Es un puntero a una funcin escribe
que ejecutar cada hebra. Tiene que ser un puntero, porque a la
hebra hay que indicarle donde comienza, es decir, la direccin
inicial del cdigo que queremos que se ejecute */
int i, salida;
char c;
int id = *(int *)idp;
if ((salida = open(argumentos[id],
O_CREAT | O_WRONLY | O_TRUNC, 0644)) <
0) {
fprintf(stderr, "Error al abrir %s\n", argumentos[id]);
pthread_exit("mal");
}
for (i=0; i<20; i++) { /*Si se consigue abrir el archivo
correctamente (se abre el archivo que indique argumentos [id]), se
espera el nmero de segundos que indique id y se escribe el carcter
correspondiente a i en cada caso (A, B C) 20 veces. Cada hebra
que acaba bien, finaliza con estado bien)
sleep(id);
c= 'A'+id-1;
write(salida, &c, 1);
}
pthread_exit("bien");
}
int main(int argc, char* argv[]) {

int i;
pthread_t hebra[MAXHEBRAS]; /* Identificador de hebra, array
donde guardamos el identificador id de cada hebra creada */
int id[MAXHEBRAS]; /* Parmetro de la hebra (para que no vare).
Es un array donde guardamos los argumentos que se le pasan a la
subrutina que ejecuta cada hebra, escribe
*/
char *estado; /* Estado en el que acabar la hebra */
argumentos= argv;
for (i=1; i<argc; i++) {
id[i-1] = i; /* Se asigna la posicin i-1 del array id, a al
argumento que se le pase a la subrutina escribe en la hebra i */
if (pthread_create(&hebra[i-1], /* Puntero al
identificador
hebra i */

de la

NULL,
escribe, / *Puntero a la direccin
del cdigo a ejecutar, la
direccin de la subrutina
escribe */
&id[i-1]) != 0) { /* Puntero al
argumento que se le
pasa como parmetro a
la subrutina escribe
que ejecuta la hebra i
*/
fprintf(stderr, "Error al crear hebra\n");
exit(1); /* Si se produce un error al crear la hebra, se
devuelve algo distinto de cero, y salimos */
}
}
for (i=1; i<argc; i++) { /* Se crean tantas hebras como
argumentos se le hayan pasado */
pthread_join(hebra[i-1], (void**)&estado); /*Puntero doble
porque le digo donde est la direccin que contiene la direccin
donde quiero que est el resultado */
printf("Terminada %s la hebra %d\n", estado, i);
} /* Este bucle espera la terminacin de cada hebra, y almacena el
estado de finalizacin en estado, pasando su direccin como
argumento, luego, lo imprime por pantalla */
exit(0);
}

Se adjunta tambin el programa forkwait.c, para poder compararlo con el programa hebras.c
forkwait.c
#include
#include
#include
#include

<unistd.h>
<fcntl.h>
<stdio.h>
<stdlib.h>

int main(int argc, char* argv[]) {


int i, j, pid, salida, estado;
char c;
for (i=1; i<argc; i++) {
if ((pid= fork())==0) {
if ((salida = open(argv[i],
O_CREAT | O_WRONLY | O_TRUNC, 0644)) <
0) {
write(2, "Error\n", 6);
exit(1);
}
for (j=0; j < 20; j++) { c=
'A' + i - 1;
write(salida, &c, 1);
sleep(i);
}
exit(0);
}
printf("Arrancado el proceso %d\n", pid);
}
for (i=1; i<argc; i++) { pid =
wait(&estado);
printf("Termina el proceso %d\n", pid);
}
exit(0);
}
Resultado de la ejecucin de hebras.c
./hebras /dev/tty /dev/tty /dev/tty &
ABACABAACBAABACABAACBAABACABAACBAABATerminada bien la
hebra 1
CBCBBCBCBBCBCBBCBTerminada bien la hebra 2
CCCCCCCTerminada bien la hebra 3
ps m (inmediatamente despus de comenzar a ejecutar el programa).
PID TTY
1501 pts/0
- - - - -

TIME CMD
00:00:00 hebras
00:00:00 00:00:00 00:00:00 00:00:00 -

1508 pts/0
- 27581 pts/0
- -

00:00:00 ps
00:00:00 00:00:00 bash
00:00:00 -

Resultado de la ejecucin de forkwait.c


./forkwait /dev/tty /dev/tty /dev/tty
AArrancado el proceso 1153
BArrancado el proceso 1154
CArrancado el proceso 1155
ABACABAACBAABACABAACBAABACABAACBAABTermina el proceso 1153
CBCBBCBCBBCBCBBCTermina el proceso 1154
CCCCCCTermina el proceso 1155
RESUMEN:
El programa comienza definiendo que se trata de un proceso en el que se garantiza
la exclusin mutua (#define _REENTRANT). De otro modo, una hebra podra llamar a una
subrutina e interrumpir a la hebra que ya est en ella, generando as errores.
El mtodo main, empieza definiendo un identificador de hebra (un array con los
identificadores de todas las hebras), y un parmetro de la hebra (array con parmetros de las
hebras, o mejor dicho, con los parmetros que se le pasan a la subrutina escribe que
ejecutar cada hebra). La longitud de estos arrays es MAXHEBRAS, constante definida
anteriormente. Tambin se define un puntero tipo char a estado.
Despus, a travs de un bucle for crea tantas hebras como argumentos se hayan
introducido al ejecutarlo, e imprime un mensaje en la salida de error si se ha producido algn
error.
Para crear la hebra, se le pasa en primer lugar el identificador (matriz
hebra), atributos null, la direccin del cdigo de la subrutina a ejecutar (escribe), y el argumento
correspondiente de la matriz id.
En un segundo bucle, se hace un join a cada hebra, es decir, el programa espera a
que terminen todas su hebras para acabar, y cuando una acaba, se imprime por pantalla
el estado en el que ha acabado. Para ello, en estado se guardar el resultado devuelto al acabar
la hebra.
El cdigo de la hebra, hace lo siguiente:
Asocia a la variable entera id el contenido de la direccin que se pasa como
argumento genrico. Abre el fichero (pasado como argumento al ejecutar el programa)
correspondiente a la hebra creada. Si hay un error, se imprime en la salida de error: se pasa mal
el parmetro.
Despus, esa hebra escribe 20 caracteres idnticos, y relacionados con el valor de su
identificador, esperando el valor de su id entre caracteres.
Por tanto, el comportamiento general del programa es escribir en los archivos que se
pasan como argumentos (en cada uno de ellos) 20 caracteres consecutivos a distintas
velocidades. (En nuestro caso, el archivo que se ha pasado como argumento, ha sido para las tres
hebras creadas el mismo, el archivo correspondiente a la pantalla, para poder ver as el resultado de
la ejecucin paso a paso por pantalla).
Si ejecutamos el programa en background observamos con ps m que el nmero de hebras es
igual al nmero de argumentos ms la hebra de control.

El programa forkwait realizaba lo mismo, pero en vez de crear un proceso con hebras entre
las que se va multiplexando el tiempo, creaba tantos procesos como
argumentos, y se obtenan los mismos resultados.

Prctica 2. Hebras en Java


Especificacin
En primer lugar, examinaremos el programa compuesto por la clase que define las hebras escritoras
(Escribe.java) y la clase principal que crea las hebras y espera a que terminen (Hebras.java).

Escribe.java
import java.io.FileOutputStream;
public class Escribe extends Thread { /* Escribe hereda todos los
mtodos y variables de la clase Thread, y aade los suyos */
protected int id;
protected String argumento; /* Definimos los argumentos
del constructor */
public Escribe(int id, String arg) {
this.id=id; argumento=arg;
} /* El constructor nicamente establece los argumentos como
variables. Uno de los argumentos se renombra, y otro se usa
utilizando this. */
public void run() {
FileOutputStream salida;
try { /*Si hay algn error en la creacin de la hebra, se
detecta con try-catch, y se imprime por pantalla el mensaje Error
en la ejecucin de +id: +e.getMessage()
salida= new FileOutputStream(argumento); /* Sirve para poder
escribir en el fichero que se le pasa como argumento */
for (int i=0; i<20; i++) {
Thread.sleep(1000*id);
salida.write('A'+id-1);
} /* Este bucle hace que se escriba 20 veces el
carcter correspondiente (A, B C) en un objeto de la clase
FileOutputStream, durmindose la hebra 1 segundo por valor id cada
vez */
} catch (Exception e) {
System.err.println("Error en la ejecucion de "+id+":
"+e.getMessage());
}
}
}

Hebras.java
public class Hebras {
static final int MAXHEBRAS=100; /* static indica que es una
variable global que puede usarse en cualquier parte, y final indica
que el valor de la variable es cosntante */
public static void main(String argv[]) {/* Mtodo main,
lo que se ejecuta */
Escribe[] hebras= new Escribe[MAXHEBRAS]; /*Se crea un array de
hebras, de la clase Escribe, de longitud 100 */
for (int i=0; i<argv.length; i++) { hebras[i]=
new Escribe(i+1, argv[i]);
hebras[i].start(); /* Se crea una hebra por cada argumento
pasado, y con el mtodo start() de la clase Thread se ejecuta el
run()(de la clase Escribe) que hace que se ejecute el cdigo de la
subrutina que debe realizar la hebra */
}
for (int i=0; i<argv.length; i++) {
try {
hebras[i].join(); /* Esperamos la finalizacin de
cada hebra */
} catch (Exception e) {
} finally { /* Con finally conseguimos que el cdigo que
hay a continuacin se ejecute, tanto si el try- catch ha capturado
algn error y ha saltado excepcin, como si no lo ha hecho, y todo ha
salido bien */
System.out.println("Terminada la hebra "+i);
}
}
}
}

Se nos pide comparar este programa con el programa de la prctica 3, hebras.c, y


que hagamos un programa implementando la interfaz Runnable:
Complelo y ejectelo de la misma manera que ejecut aqul, explicando su
funcionamiento y resultados. Use ps m para ver las hebras asociadas a cada proceso. Observe que
hay bastantes hebras adicionales para soportar la mquina virtual Java, que no hemos programado
explcitamente.
Haga un programa de comportamiento idntico, pero implementando la interfaz Runnable.
En primer lugar, adjuntamos el resultado de la ejecucin del programa Hebras.java, as
como el proceso de compilacin premio, con la mquina virtual java:
javac Escribe.java
javac Hebras.java
java Hebras /dev/tty /dev/tty /dev/tty
ABACABAACBAABACABAACBAABACABAACBAABATerminada la hebra 0
CBCBBCBCBBCBCBBCBTerminada la hebra 1
CCCCCCCTerminada la hebra 2

10

Si ejecutamos en background, podemos ver, con ps m las hebras asociadas a cada


proceso, y comprobamos que efectivamente, hay muchas hebras adicionales, como se nos comentaba en
el enunciado, para soportal la mquina virtual java.
ps m (inmediatamente despus de comenzar la ejecucin).
PID
2312
2328
27581
-

TTY
pts/0
pts/0
pts/0
-

TIME
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00
00:00:00

CMD
java
ps
bash

Se adjunta ahora el cdigo del programa creado, implementando la interfaz Runnable:


EscribeModificado.java
import java.io.FileOutputStream;
public class EscribeModificado implements Runnable {
protected int id;
protected String argumento;
public EscribeModificado(int id, String arg) {
this.id=id; argumento=arg;
}
public void run() {
FileOutputStream salida;
try {
salida= new FileOutputStream(argumento);
for (int i=0; i<20; i++) {
Thread.sleep(1000*id);
salida.write('A'+id-1);
}
} catch (Exception e) {
System.err.println("Error en la ejecucion de "+id+":
"+e.getMessage());
}
}
}

11

HebrasModificado.java
public class HebrasModificado {
static final int MAXHEBRAS=100;
public static void main(String argv[]) {
Thread[] hebras= new Thread[MAXHEBRAS];
for (int i=0; i<argv.length; i++) { EscribeModificado esc=
new EscribeModificado(i+1,
argv[i]); hebras
[i]= new Thread(esc); hebras[i].start();
}
for (int i=0; i<argv.length; i++) {
try {
hebras[i].join();
} catch (Exception e) {
} finally {
System.out.println("Terminada la hebra "+i);
}
}
}
}
Comprobamos que el resultado de la ejecucin es el mismo que con Hebras.java:
javac EscribeModificado.java
HebrasModificado.java
HebrasModificado /dev/tty /dev/tty /dev/tty
ABACABAABCAABACABAACBAABACABAACBAABATerminada la hebra 0
CBCBBCBCBBCBCBBCBTerminada la hebra 1
CCCCCCCTerminada la hebra 2
RESUMEN:
El programa Hebras.java hace lo mismo que el de la prctica 3 (Hebras.c),
esto es, por cada argumento introducido por la lnea de comandos, se crea una hebra que escribir 20
caracteres iguales, y relacionados con su identificador, de modo que tambin basndose en el
identificador, se har a velocidades diferentes.
De todos modos, al realizarse en java y no en C, existen algunas diferencias
con respecto al programa de la prctica anterior:

las hebras una vez creadas, necesitan ejecutar start() para que empiecen a ejecutarse.
el cdigo de la subrutina que tiene que ejecutar la hebra, no se pasa como argumento,
sino que la hebra se crea como un objeto de la clase que va a ejecutar.
las hebras en java no devuelven mensajes a travs de join o exit, por tanto, tenemos
que basarnos en la captura de errores de java para obtener resultados de terminacin.
en java se protegen las variables de identificacin de la hebra y argumentos para que
no se pueda acceder a ellos desde otras clases generando as

errores.

12

Pregunta 5 (2,5 puntos) Sept 2009


La prctica 2 (Hebras en Java) incluye la clase Escribe donde se definen las hebras del programa. El
mtodo run de esta clase tiene un bucle que se ejecuta 20 veces; en cada iteracin, la hebra
correspondiente se queda dormida un determinado intervalo de tiempo y despus escribe una letra en
un fichero, cuyo nombre se ha proporcionado al invocar al constructor de la clase.
Se quiere modificar esa clase, de manera que mientras est dormida la hebra, suene un cierto
fichero de audio;
uno especfico para cada hebra. Para implementar esta funcionalidad, se va a utilizar la clase
ReproductorSonido:
public c l a s s ReproductorSonido {

protected S t r i n g nombreFichero ;

ReproductorSonido.java

p riv a t e jav a . a p p l e t . AudioClip c l i p = n u l l ;

/ / Se i n i c i a l i z a e l c l i p de a u d i o que s e q u i e r e r e p r o d u c i r
p r o t e c t e d v o i d c r e a C l i p ( ) t h row s j a v a . n e t . M alf or m edU RL E xcept io n {
c l i p = j a v a . a p p l e t . A p p l e t . n e w A u d i o C l i p ( new j a v a . n e t . URL( " f i l e : " + n o m b r e F i c h e r o )
);
}

5
6
7

8
9

//
P e r m i t e que e l c l i p c o m i e n c e a s o n a r y l o d e j a s o n a n d o de f o n d o ,
//
r e t o r n a n d o de i n m e d i a t o
protected void a rran ca S o ni do ( ) {
i f ( c l i p != n u l l ) c l i p . p la y ( ) ;
}

10
11
12
13
14
15

/ / Para e l s o n i d o d e l c l i p ( s o l o e l d e l c l i p a s o c i a d o a l o b j e t o c l i p )
protected void paraSonido ( ) {
i f ( c l i p != n u l l ) c l i p . s t o p ( ) ;
}

16
17
18
19
20

A continuacin se muestra la clase EscribeConSonido, que es una versin modificada de la


clase original
Escribe para arrancar el clip de sonido (hemos incluido comentarios en todas las lneas que tienen
modificaciones):
imp ort j a v a . i o . F i l e O u t p u t S t r e a m ;

1
2

EscribeConSonido.java
3
4
5
6

p u b l i c c l a s s E s c r i b e C o n S o n i d o e x t e n d s R e p r o d u c t o r S o n i d o imp lemen t s R u n n a b l e {
/ / E s c r i b e C o n S o n i d o no h e r e d a de T h r e a d
protected i n t id ;
p r o t e c t e d S t r i n g argu mento ;

7
8
9
10
11

12
13
14
15
16

17
18

/ / a r g : nombre d e l f i c h e r o de a u d i o s i n e x t e n s i n
pu bl i c EscribeConSonido ( i n t id , S t r i n g arg ) {
t h i s . i d = i d ; argumento = a r g ;
n o m b r e F i c h e r o = a r g + " . a i f " ; / / e l f i c h e r o que va a s o n a r t e n d r e l nombre p r o p
orcionado
/ / en a r g p e r o con e x t e n s i n . a i f
try {
cr e aC lip ( ) ; / / se crea e l c l i p
} catch ( Ex ception e ) {
S ys tem . e r r . p r i n t l n ( " E r r o r en l a e j e c u c i o n de " + i d + " : " + e . g e t M e s s a
ge ( ) ) ;
}
}

19
20
21
22
23
24
25

public void run ( ) {


FileOutputStream s al i d a ;
try {
s a l i d a = new F i l e O u t p u t S t r e a m ( a r g u m e n t o ) ;
f o r ( i n t i = 0 ; i < 2 0 ; i ++) {
arrancaSonido ( ) ;
/ / se arranca e l sonido del c l i p

13

Thread . s l e e p ( 4 0 0 0 ) ;
/ / sonar 4 segundos
s a l i d a . w r i t e ( A + i d 1) ;
paraSonido ( ) ;
/ / se d e ti en e la reproduccin del c l i p

26
27
28

}
} catch ( Ex ception e ) {
S ys tem . e r r . p r i n t l n ( " E r r o r en l a e j e c u c i o n de " + i d + " : " + e . g e t M e s s a
ge ( ) ) ;
}

29
30
31

32

33

34

A continuacin se muestra la clase Hebras, en la que se ha cambiado el mtodo Escribe por


EscribeConSonido.
Hebras.java
1

p u b l i c c l a s s Hebras {

s t a t i c f i n a l i n t MAXHEBRAS= 1 0 0 ;

3
4

p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
E s c r i b e C o n S o n i d o [ ] h e b r a s = new E s c r i b e C o n S o n i d o [MAXHEBRAS ] ;
f o r ( i n t i = 0 ; i < a r g v . l e n g t h ; i ++) {
h e b r a s [ i ] = new E s c r i b e C o n S o n i d o ( i +1 , a r g v [ i ] ) ;
hebras [ i ] . s t a r t ( ) ;
}
f o r ( i n t i = 0 ; i < a r g v . l e n g t h ; i ++) {
try {
hebras [ i ] . j o i n ( ) ;
}
c a t c h ( E x c e p t i o n e ) {}
finally {
S ys tem . o u t . p r i n t l n ( " T e r m i n a d a l a h e b r a " + i ) ;
}
}
}

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Cuando se compila esta clase, se obtiene el siguiente mensaje de error en la lnea 9:


The method start() is undefined for the type EscribeConSonido

Subpregunta 5.1 (0.7 puntos)


Corregir la clase Hebras (solo esa clase) para que el programa compile correctamente.
La clase EscribeConSonido ya no hereda de Thread y por eso hay que cambiar la forma de
construir las hebras.
1

p u b l i c c l a s s H ebr as {

Hebras.java
2

s t a t i c f i n a l i n t MAXHEBRAS=100;

3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18

p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
T h r e a d [ ] h e b r a s = new T h r e a d [MAXHEBRAS] ;
f o r ( i n t i =0; i <a r g v . l e n g t h ; i ++) {
h e b r a s [ i ]= new T h r e a d ( new E s c r i b e C o n S o n i d o ( i +1 , a r g v [ i ] ) )
;
hebras [ i ] . s t a r t ( ) ;
}
f o r ( i n t i =0; i <a r g v . l e n g t h ; i ++) {
try {
hebras [ i ] . j o i n ( ) ;
}
catch ( Exception e ) {
}
finally {
S y s t e m . o u t . p r i n t l n ( " T e r m i n a d a l a h e b r a " +( i + 1 ) ) ;
}

14

19

20
21

Subpregunta 5.2 (1.8 puntos)


Java permite tener varios clip sonando al mismo tiempo (de hecho eso es lo que hace esta
versin). Para ello las clases de la biblioteca de Java van entremezclando los sonidos. Se quiere
cambiar la clase EscribeConSonido para que un clip solo arranque cuando no haya otro sonando. La
lnea 24 solo podr arrancar el clip, si no hay otro sonando. Si lo hubiese, esperara a que termine (la
lnea 27 detiene la reproduccin del clip). De esta forma un clip sonar 4 segundos; despus de esos 4
segundos podr sonar otro diferente.
Modificar la clase EscribeConSonido para que no haya ms de un clip sonando en cada momento,
de acuerdo
con la descripcin en el prrafo previo. Se puede utilizar cualquiera de los mtodos de exclusin
mutua de Java, incluyendo el desarrollo de una clase monitor.
Para garantizar el acceso a la reproduccin de sonido empleamos un mutex
implementado con los monitores de Java:
1

p u b l i c c l a s s MutexSonido {

MutexSonido.java
p r o t e c t e d b o o l e a n ocupado = f a l s e ;

3
4

pu blic synchronized void arrancaSonido ( ) throws I n t e r r u p t e d E x c e p t i o n {


i f ( ocupado ) w a i t ( ) ;
ocupado = t r u e ;
}

5
6
7
8
9

p u b lic s ynchr oniz ed void paraSonido ( ) {


ocupado = f a l s e ;
notify ();
}

10
11
12
13
14

Mediante ese mutex garantizamos en EscribeConSonido el acceso exclusivo a la


reproduccin de sonido:
1

import java . io . FileOutputStrea m ;

EscribeConSonido.java
3

p u b l i c c l a s s E scr ibeConSonido ex te n d s ReproduceSonido implem ents Runnable {

4
5
6
7

protected i nt id ;
p r o t e c t e d S t r i n g argumento ;
p r o t e c t e d MutexSonido mutex ;

8
9
10
11
12
13
14
15

16
17

p u b l i c E s c r i b e C o n S o n i d o ( i n t i d , S t r i n g arg , M u t e x S o n i d o m u t e x ) {
t h i s . i d = i d ; a r g u m e n t o =a r g ; n o m b r e F i c h e r o =a r g+" . a i f " ;
t h i s . m u t e x=m u t e x ;
try {
creaClip ( ) ;
} catch ( Exception e ) {
S y s t e m . e r r . p r i n t l n ( " E r r o r 1 en l a e j e c u c i o n de "+ i d +" : "+e . g e t
Message ( ) ) ;
}
}

18
19
20
21
22
23

p u b l i c void run ( ) {
FileOutputStream s alid a ;
try {
s a l i d a = new F i l e O u t p u t S t r e a m ( a r g u m e n t o ) ;
f o r ( i n t i =0; i <10; i ++) {

15

mutex . a r r a n c a S o n i d o ( ) ;
arrancaSonido ( ) ;
Thread . s l e e p ( 4 0 0 0 ) ;
s a l i d a . w r i t e ( A +i d 1);
paraSonido ( ) ;
mutex . pa ra So nid o ( ) ;
Thread . s l e e p ( 1 0 0 ) ;

24
25
26
27
28
29
30

}
} catch ( Exception e ) {
S y s t e m . e r r . p r i n t l n ( " E r r o r 2 en l a e j e c u c i o n de "+ i d +" : "+e . g e t
Message ( ) ) ;
e . printStackTrace () ;
}

31
32
33

34
35

36
37

Finalmente necesitamos que el mtodo main de la clase Hebras cree el mutex que las
hebras compar- tirn, y lo pase como parmetro en la construccin de las clases
EscribeConSonido:
1

p u b l i c c l a s s H ebr as {

Hebras.java
s t a t i c f i n a l i n t MAXHEBRAS=100;

2
3

p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
M u t e x S o n i d o m u t e x=new M u t e x S o n i d o ( ) ;
T h r e a d [ ] h e b r a s = new T h r e a d [MAXHEBRAS] ;
f o r ( i n t i =0; i <a r g v . l e n g t h ; i ++) {
h e b r a s [ i ]= new T h r e a d ( new E s c r i b e C o n S o n i d o ( i +1 , a r g v [ i ] , m u t e x
));
hebras [ i ] . s t a r t ( ) ;
}
f o r ( i n t i =0; i <a r g v . l e n g t h ; i ++) {
try {
hebras [ i ] . j o i n ( ) ;
}

4
5
6
7
8

9
10
11
12
13
14

catch ( Exception e ) {
}
finally {
S y s t e m . o u t . p r i n t l n ( " T e r m i n a d a l a h e b r a " +( i + 1 ) ) ;
}

15
16
17
18
19

20

21
22

16

Prctica 4. Comunicacin sincronizada con tuberas


Especificacin
Nos piden que ejecutemos el programa ppipe1.c que se adjunta a continuacin, y que
observemos como el productor y el consumidor se intercambian informacin.
ppipe1.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void productor(int esc) { /*la funcin productor no devuelve nada,
se le pasa como parmetro un entero, que ser el D.F. del fichero
donde escribir */
int i;
for (i=1; i<=10; i++) {
write(esc, &i, sizeof i); sleep(1); /* Bucle de ejecucin write
(int df, char* Buf, int NumBytes), despus, se duerme durante 1
segundo */
}
exit(0);
}
void consumidor(int lec) {
int leidos, i;
while ((leidos = read(lec, &i, sizeof i)) > 0)
printf("Recibido %d\n", i); /* va saliendo por pantalla lo que lee
el consumidor, que est (en cada caso) en &i (Recibido 1, Recibido 2,
Recibido 2... Recibido 10) */
exit(0);
}
int main(void) {
int pid, tubo[2];
pipe(tubo); /* Se crea la tubera, que es un array de dos enteros,
el primer entero (0) es el d.f de lectura, y el segundo entero (1)
es el d.f de escritura */
if ((pid= fork())==0) {
close(tubo[1]);
consumidor(tubo[0]);
} /*Se crea un hijo, que ser el consumidor. Como el consumidor
slo leer, cerramos el d.f tubo [1] (cerramos el extremo del tubo de
escritura), para evitar que procesos que quieran escribir se queden
bloqueados al haber an una tubera con entrada de escritura cuando
finalice el consumidor */
else {
close(tubo[0]);

17

productor(tubo[1]);
} /* El padre ser el productor, que cierra el d.f de lectura,
tubo[1], para evitar que otros procesos que quieran leer se bloqueen
*/
}
La ejecucin del programa ppipe1.c es la siguiente:
./ppipe1
Recibido 1
Recibido 2
Recibido 3
Recibido 4
Recibido 5
Recibido 6
Recibido 7
Recibido 8
Recibido 9
Recibido 10
Se nos pide ahora que probemos (ejecutemos), una modificacin de ppipe1.c, que se
llama ppipe2.c, en la que productor y consumidor son ambos hijos (antes el productor era el
padre y el consumidor era el hijo) del proceso principal, esperando ste por la terminacin de ambos.
ppipe2.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void productor(int esc) {
int i;
for (i=1; i<=10; i++) {
write(esc, &i, sizeof i); sleep(1);
}
exit(0);
}
void consumidor(int lec) {
int leidos, i;
while ((leidos = read(lec, &i, sizeof i)) > 0) printf("Recibido
%d\n", i);
exit(0);
}
int main(void) {

int pid, tubo[2];


pipe(tubo);
if ((pid= fork())==0) {
close(tubo[1]); consumidor(tubo[0]);
} /* Creamos el primer hijo, que ser el consumidor, y si le toca
ejecutar al hijo, se cierra el d.f de escritura de este hijo, ya que
el consumidor slo escribir */

18

else { /* si no, se cierra el d.f de lectura del padre, y se crea


otro hijo que ser el productor, y que heredar la condicin de tener
el d.f de lectura cerrado */
close(tubo[0]);
if ((pid= fork())==0) productor(tubo[1]);
}
wait(NULL);
wait(NULL); /* El padre espera a que terminen los dos procesos hijo
que se estn ejecutando concurrentemente */
}
Observe que en este caso el programa no termina nunca. Ello es debido a que ahora hay
tres copias de la variable pipe, una en cada proceso. El consumidor no se da por enterado de la
terminacin del productor hasta que no se hayan cerrado todas las copias del extremo de escritura
de la tubera. Una la cierra el productor implcitamente cuando termina (con exit). La otra se cierra
explcitamente antes de llamar al consumidor. Sin embargo el proceso padre no la cierra y por ello el
consumidor queda esperando. Modifique el programa para que terminen todos los procesos
cuando el productor termine.
En primer lugar veremos que, efectivamente, el programa no acaba nunca:
./ppipe2
Recibido 1
Recibido 2
Recibido 3
Recibido 4
Recibido 5
Recibido 6
Recibido 7
Recibido 8
Recibido 9
Recibido 10
...
...
Como vemos, funciona igual que el programa ppipe1.c, pero sin terminar nunca la ejecucin.
A continuacin, se adjunta el programa ppipe3.c, una modificacin de ppipe2.c, para que
terminen todos los procesos cuando el productor termine.
ppipe3.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void productor(int esc) {
int i;
for (i=1; i<=10; i++) {
write(esc, &i, sizeof i); sleep(1);
}
exit(0);
}
void consumidor(int lec) {
int leidos, i;

19

while ((leidos = read(lec, &i, sizeof i)) > 0) printf("Recibido


%d\n", i);
exit(0);
}
int main(void) { int
pid, tubo[2];
pipe(tubo);
if ((pid= fork())==0) {
close(tubo[1]); consumidor(tubo[0]);
}
else {
close(tubo[0]);
if ((pid= fork())==0){ productor(tubo[1]);
}
close(tubo[1]); /*Esta es la instruccin que hemos aadido, para
que el padre cierre el d.f de escritura de su pipe, y as, el
consumidor se entere de que el productor ha acabado */
}
wait(NULL);
wait(NULL);
}
A continuacin se adjunta el resultado de la ejecucin de ppipe3.c
./ppipe3
Recibido 1

Recibido 2
Recibido 3
Recibido 4
Recibido 5
Recibido 6
Recibido 7
Recibido 8
Recibido 9
Recibido 10
RESUMEN:
El programa ppipe1.c se basa en la creacin de un pipe (tubera) en el que va a escribir
un productor y u del que va a leer un consumidor. El programa implementa, por tanto, los
mtodos correspondientes a ellos. El productor escribe los nmeros del 1 al 10, y espera un
segundo entre cada uno de ellos. El consumidor por su parte, lee del pipe hasta que est
vaco e imprime por pantalla lo que va leyendo.
Para crear el pipe se ejecuta la instruccin pipe (tubo), siendo tubo un vector de longitud
2. Despus de ejecutar la instruccin anterior, en la posicin primera del vector (tubo[0]) se
encontrar el descriptor que hay que utilizar para leer del pipe, y en la segunda posicin del
vector (tubo[0]) se encontrar el descriptor para la escritura.
Como los pipes no tienen nombre, no se les puede identificar ni se puede solicitar su
array, por tanto, para que pueda haber lectura y escritura con dos procesos sobre l, deber
heredarse. Por eso, despus de ejecutar lo anterior, se crea el proceso hijo.

20

Posteriormente, se mira el valor del identificador (pid) del proceso, para ver si al que se
le ha concedido la ejecucin es el padre o es el hijo.
Si es el padre, cerramos el descriptor de lectura en el pipe, ya que si no, puede llevar a
errores. Despus de lo anterior, se ejecuta una llamada al productor basado en el descriptor de lectura,
que hace lo ya explicado anteriormente. Finalmente, al hacer exit (0), se cierra el descriptor de
escritura tambin.
Si el proceso al que le toca ejecutar es hijo, ser el consumidor, y por tanto, se cierra el
descriptor correspondiente a escritura en el pipe y se llama al consumidor, pasando como
parmetro el descriptor de lectura. Al llegar a la instruccin read, pueden pasar varias cosas:
que no haya nada en el pipe pero existan procesos que puedan escribir: se bloquea el
consumidor hasta que el otro proceso produzca.
que haya n datos y nadie ms pueda producir: se devuelven los n y se acaba.
que no haya datos y no se pueda producir ms: se termina.
Finalmente, al igual que antes, al ejecutar exit (0), se cierra el descriptor de lectura.
Es importante cerrar los descriptores, porque si no, algn proceso puede pensar que todava
hay algn proceso que puede escribir o leer y el programa no acabara nunca (como ocurre con
ppipe2.c).

21

22

Prctica 5. Recubrimientos y redirecciones


Especificacin
Estudie y pruebe el exec/pipeline.c, que combina dos programas externos por medio de
una tubera, cuyos extremos estn conectados convenientemente a la salida estndar de uno y a la
entrada estndar del otro. Compruebe que su resultado es el mismo que el de ejecutar awk -F:
'{print $5}' /etc/passwd | sort. Recuerde que sort ordena lneas y awk procesa texto
estructurado en campos. En este caso el separador de campos es : y la orden de procesar, {print
$5}, dice que se obtenga el quinto campo (ntese que el entrecomillado es necesario en la shell, ya
que la orden tiene los metacaracteres {}$ y un espacio).
A continuacin se adjunta el programa pipeline.c
pipeline.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void productor(void) {
execlp("awk", "awk", "-F:", "{print $5}", "/etc/passwd", NULL);
/* El productor ejecuta el fichero awk (awk:procesa el texto
estructurado en campos), que se le pasa dos veces (awk, awk),
porque segn la estructura de execlp hay que hacerlo as, el primer
awk es el fichero que hay que ejecutar, y el segundo awk
corresponde al primer argumento arg[0], que es el nombre del
fichero (awk).La opcin -F: indica el tipo de separacin entre
campos, en este caso sern :. {print $5} nos indica que hay que
obtener el 5 campo, /etc/passwd es la ruta del fichero de
entrada, de donde se toma el texto. NULL nos indica el final de los
argumentos */
perror("execlp"); exit(1);
}
void consumidor(void) {
execlp("sort", "sort", NULL); /* El consumidor ejecuta el fichero
sort, que ordena cadenas de caracteres, por defecto,
alfabticamente, y las saca por pantalla */
perror("execlp"); exit(1);
}
int main(void) {
int pid, tubo[2];
pipe(tubo); /* Creamos la tubera */
if ((pid= fork())==0) { /* El hijo ser el productor, el padre ser
el consumidor */
close(tubo[0]); dup2(tubo[1], 1); close(tubo[1]); /* Se cierra el
d.f tubo [0], ya que el productor no lee, slo escribe, y luego
dup2(tubo[1],1) duplica el d.f tubo[1], que corresponde a escritura
y que se redirige a la salida estndar (d.f 1, coincide con la
pantalla), y despus se cierra el d.f. tubo[1], para no dejar nada
abierto. Ahora escribir en la salida estndar, es como escribir en
el extremo de escritura del pipe */

23

productor();
}
else {
close(tubo[1]); dup2(tubo[0], 0); close(tubo[0]); /* Se cierra el
d.f tubo [1], ya que el consumidor slo lee, no escribe, y despus
se duplica el d.f tubo[0] corresponde a lectura el cual se redirige a
entrada estndar (d.f. 0, coincide con el teclado), y se cierra d.f.
tubo[0] (el antiguo, el que haba antes de que se duplicara, para no
dejarlo abierto y evitar bloqueos. Ahora, leer de la entrada
estndar, es como leer del extremo de lectura del pipe */
consumidor();
}
}
A continuacin se adjunta el resultado de la ejecucin de pipeline.c
./pipeline
added by portage for apache added by
portage for bind
added by portage for cronbase added by
portage for dbus
added by portage for hal
added by portage for mldonkey added by
portage for mysql
added by portage for nagios-core added
by portage for ntp
added by portage for openldap added by
portage for openssh added by portage for
portmap added by portage for postfix
added by portage for postgresql adm
bin
daemon
halt
lp mail man
news
nobody
operator
portage
postmaster
root shutdown
smmsp
sync
uucp
Se nos pide hacer una modificacin del programa pipeline.c para que ordene en orden
alfabtico inverso (opcin r del programa sort). Esta modificacin se adjunta a continuacin:

pipelineInverso.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void productor(void) {

24

execlp("awk", "awk", "-F:", "{print $5}", "/etc/passwd", NULL);


perror("execlp"); exit(1);
}
void consumidor(void) {
execlp("sort", "sort","-r", NULL); /* La nica modificacin que
hemos hecho ha sido incluir la opcin -r de sort, para que ordene
las cadenas de caracteres en orden alfabtico inverso, el resto del
programa, es exactamente igual que pipeline.c */
perror("execlp"); exit(1);
}
int main(void) {
int pid, tubo[2];
pipe(tubo);
if ((pid= fork())==0) {
close(tubo[0]); dup2(tubo[1], 1); close(tubo[1]); productor();
}
else {
close(tubo[1]); dup2(tubo[0], 0); close(tubo[0]); consumidor();
}
}
El resultado de la ejecucin de pipelineInverso.c es el siguiente:
./pipelineInverso
uucp
sync
smmsp
shutdow
n root
postmaster portage
operator
nobody
news
man
mail
lp
halt
daemon
bin
adm
added by portage for postgresql
added by portage for postfix
added by portage for portmap
added by portage for openssh
added by portage for openldap
added by portage for ntp
added by portage for nagios-core
added by portage for mysql
added by portage for mldonkey
added by portage for hal
added by portage for dbus

25

added by portage for cronbase


added by portage for bind
added by portage for apache
RESUMEN:
El programa pipeline.c hace bsicamente lo mismo que los programas vistos en la prctica 4,
pero con algunas modificaciones.
El programa comunica 2 procesos, padre e hijo, a travs de un pipe. El padre ser el
consumidor, por tanto, cuando se le asigne tiempo de ejecucin cerrar el descriptor de escritura
(al igual que en la prctica 1), pero ahora el extremo de lectura del pipe a la entrada estndar del
proceso mediante la instruccin dup2. As, al leer de la entrada estndar, estamos leyendo del extremo
de lectura del pipe. Despus, cierra el descriptor de lectura del pipe, para evitar bloqueos de
procesos. Despus de ejecuta el cdigo de consumidor, que hace una sustitucin de la imagen del
proceso por la de sort, que hace que lo que se extraiga del pipe se escriba ordenado por la salida
estndar.
El hijo, por su parte, ser el productor, y por tanto, cerrar el descriptor de lectura del
pipe (tubo[0]), despus redirigir el extremo de escritura del pipe (con dup2) a la salida estndar, por
lo que ahora, si escribimos en la salida estndar, estamos escribiendo en el extremo de escriturad
del pipe. Despus se cerrar el descriptor de escritura del pipe. El mtodo productor comienza
sustituyendo la imagen del proceso por la de akw con las opciones -F, {print $5}, /etc/passwd,
NULL. Y escribe el fichero segn los parmetros indicados.
Por tanto, podemos comprobar, que el programa hace lo mismo que :
awk -F: '{print $5}' /etc/passwd | sort

26

Pregunta 3 (2,5 puntos) Dic 2012


Se quiere modificar el programa de la practica 5 (Recubrimientos y redirecciones) para ejecutar:
grep Jorge /etc/passwd | grep Almansa | grep Aguirre
El programa grep admite dos parmetros: un patrn de texto y el nombre de un fichero, que es opcional.
Este programa escribe las lneas de entrada que contienen el patrn proporcionado en el primer
parmetro. Las lneas se leen del fichero que se proporciona como segundo parmetro. Si no se
proporciona, se leen de la entrada estndar.
Se pide un nuevo programa que permita ejecutar la combinacin previa de los tres grep, teniendo en
cuenta las siguientes consideraciones:
Se debe crear un proceso para ejecutar cada
grep.
El proceso inicial del programa esperar la terminacin de los grep, escribiendo mensajes en el caso de
que se produzcan errores de ejecucin (por ejemplo, cuando grep se termina con error porque el fichero
no existe).
Los descriptores de las tuberas deben ser solo accesibles para los procesos que los deban
utilizar.
# i n c l u d e < s t d i o . h>
# i n c l u d e < u n i s t d . h>
# i n c l u d e < s t d l i b . h>
4
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

15
16
17
18
19
20
21
22
21
22
19
20
21
22
23
24
25
26
27
28
29
30
31

v o i d e x e c _ g r e p ( i n t t ub o E n t r a d a [ ] , i n t t u b o S a l i d a [ ] , char pat r on , char f i c h e r o ) {


i f ( t u b o E n t r a d a != NULL ) {
close (0);
dup2 ( t u b o E n t r a d a [ 0 ] , 0 ) ;
c los e ( tuboEntrada [ 0 ] ) ;
c los e ( tuboEntrada [ 1 ] ) ;
}
i f ( t u b o S a l i d a != NULL ) {
close (1);
dup2 ( t u b o S a l i d a [ 1 ] , 1 ) ;
close ( tuboSalida [0]);
close ( tuboSalida [1]);
}
i f ( f i c h e r o != NULL ) {
e x e c l p ( " / b i n / g r e p " , " / b i n / g r e p " , p a t r o n , f i c h e r o , NULL ) ;
exit (1);
} else {
e x e c l p ( " / b i n / g r e p " , " / b i n / g r e p " , p a t r o n , NULL ) ;
exit (1);
}
}
i n t main ( i n t a r g c , c h a r a r g v [ ] ) {
i n t tubo1 [ 2 ] ;
i n t tubo2 [ 2 ] ;
i n t pid ;
i n t pid2 ;
i n t pid3 ;
i nt cpid ;
int stat , i ;
pipe ( tubo1 ) ;
pipe ( tubo2 ) ;
i f ( ( p i d = f o r k ( ) ) == 0 ) {
c l o s e ( tubo2 [ 0 ] ) ;
c l o s e ( tubo2 [ 1 ] ) ;
e x e c _ g r e p ( NULL , t u b o 1 , " J o r g e " , " / e t c / p a s s w d " ) ;
} e l s e i f ( ( p i d 2 = f o r k ( ) ) == 0 ) {
e x e c _ g r e p ( t u b o 1 , t u b o 2 , " Al ma ns a " , NULL ) ;
} e l s e i f ( ( p i d 3 = f o r k ( ) ) == 0 ) {
c l o s e ( tubo1 [ 0 ] ) ;
c l o s e ( tubo1 [ 1 ] ) ;
e x e c _ g r e p ( t u b o 2 , NULL , " A g u i r r e " , NULL ) ;
} else {
c l o s e ( tubo1 [ 0 ] ) ;
c l o s e ( tubo1 [ 1 ] ) ;

27

c l o s e ( tubo2 [ 0 ] ) ;
c l o s e ( tubo2 [ 1 ] ) ;
f o r ( i =0; i < 3 ; i ++) {
c p i d = w a i t (& s t a t ) ;
i f ( ( c p i d != p i d ) && ( c p i d != p i d 2 ) && ( c p i d != p i d 3 ) )
d p r i n t f ( 2 , " end g r e p e r r o r \ n " ) ;
i f ( s t a t != 0 )
d p r i n t f ( 2 , " g r e p e r r o r %i \ n " , s t a t ) ;
}
}

32
33
24
25
26
27
28
29
30
31

28

Prctica 6. Dibujo de imgenes contenidas en ficheros con pipes


Especificacin
Haga un pequeo programa que presente, separadas por intervalos de tiempo de 3 segundos, una serie de
imgenes pasadas como parmetros. Utilice los mecanismos de comunicacin entre procesos de Unix
pinta.c
#include
#include
#include
#include

<unistd.h>
<stdio.h>
<stdlib.h>
<signal.h>

void productor(char *imagen) {


//Enviamos a la salida estndar el contenido
//de la imagen deseada
execlp("cat", "cat",imagen, NULL);
perror("execlp");
exit(1);
}
void consumidor(void) {
//Cogemos la imagen de la entrada estndar y
//la mostramos
execlp("display", "display", "-", NULL);
perror("execlp");
exit(1);
}
int main (int argc, char *argv[]) {
int pid1, pid2, tubo[2],i;
for(i=1;i<argc;i++){
pipe(tubo);
if ((pid1= fork())==0) { //El primer hijo "display"
close(tubo[1]); dup2(tubo[0], 0); close(tubo[0]);
consumidor();
}
else {
if ((pid2= fork())==0) { //El sengundo "cat"
close(tubo[0]); dup2(tubo[1], 1);
close(tubo[1]);
productor(argv[i]);
}else{
close(tubo[1]); //Cerramos el tubo, necesario
sleep(3); //Esperamos 3 segundos
kill(pid1, SIGTERM); //Matamos a los hijos
kill(pid2, SIGTERM);
wait(); //Nos aseguramos de que no son zombies
wait();
}
}
}
exit(0); //Terminamos con todo bien
}

29

Pregunta 5 (3 puntos) Sept 2004


Modifique la praactica 6 (pinta.c ) para que en lugar de borrar cada imagen antes
de pintar la siguiente, la imaagenes se vayan apilando una encima de otra, y cada una de
ellas vaya desapareciendo al cabo de 10 segundos de aparecer. Explique lo que hace y
entregue el listado con las modificaciones.
Ahora no hay que matar cada proceso de display . Sin embargo el pro- ceso
que dibuja ahora se convierte en dos: uno que dibuja y otro que espera 10
segundos y lo mata.
/* Se eliminan los asesinatos y esperas por terminacion:
kill(pid, SIGTERM);
waitpid(pid, &estado, 0);
*/
/* y se reemplaza el proceso que dibujaba:
execlp(display, display, -,
NULL); Error(exec);
exit(1);
*/
/* por un par de procesos: uno que dibuja y otro que lo mata al rato.*/
int pdibuja;
if ((pdibuja= fork())==0) {
execlp("display" , "display" , "-" , NULL);
Error("exec" );
exit(1);
}
sleep(10);
kill(pdibuja, SIGTERM);
waitpid(pdibuja, &estado,
0);
exit(0);

30

Pregunta 4 (2,5 puntos) Jun 2011


El enunciado de la prctica 6 (Dibujo de imgenes contenidas en ficheros usando pipes) solicita
hacer un pequeo programa que presente, separadas por intervalos de tiempo de 3 segundos, una serie de
imgenes pasadas como parmetros. La solucin debe emplear los mecanismos de comunicacin entre
procesos de Unix.
En este ejercicio se supone que se tienen todos los ficheros de imgenes en un fichero
comprimido. En el
directorio de trabajo nicamente se dispone de este fichero y del programa pinta.
Se pide modificar el programa pinta.c para ejecutarlo de la misma forma que en la prctica, pero
teniendo en cuenta cmo se tienen los ficheros en este caso. Cuando se invoca este programa, el primer
argumento es el fichero comprimido donde estn las imgenes que hay que mostrar. Un ejemplo de cmo
invocar a este programa es:
./pinta animales.zip barbo.jpeg erizo.jpeg

Para implementar esta versin del programa pinta.c se emplear el programa unzip. El acceso al
fichero de imagen a mostrar se debe extraer del fichero comprimido y enviarlo al programa adecuado
mediante una tubera. A modo de ilustracin de lo que se pide, una operacin equivalente realizada
desde la lnea de rdenes sera:
unzip -p animales.zip fichero_a_extraer | otro_programa

donde fichero_a_extraer es uno de los ficheros que el usario quiere mostrar y otro_programa es el
programa que recibe este fichero.
# i n c l u d e < u n i s t d . h>
# i n c l u d e < s t d i o . h>
# i n c l u d e < s t d l i b . h>
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

23
24
25
26
27
28
29
30
31

v o i d p r o d u c t o r ( c h a r f i c h C o m p r i m i d o , c h a r image n ) {
e x e c l p ( " u n z i p " , " u n z i p " , "p " , f i c h C o m p r i m i d o , image n , NULL ) ;
perror ( " execlp " );
exit (1);
}
void consumidor ( void ) {
e x e c l p ( " d i s p l a y " , " d i s p l a y " , "" , NULL ) ;
perror ( " execlp " );
exit (1);
}
i n t main ( i n t ar gc , c h a r a r g ) {
i n t pid , pid2 , i , tubo [ 2] ;
f o r ( i =2; i < a r g c ; i ++) {
pipe ( tubo ) ;
i f ( ( p i d= f o r k ( ) ) = = 0 ) {
c l o s e ( t u b o [ 0 ] ) ; dup2 ( t u b o [ 1 ] , 1 ) ; c l o s e ( t u b o [ 1 ] ) ;
prod uctor ( arg [ 1] , arg [ i ] ) ;
}
else {
i f ( ( p i d 2 = f o r k ( ) ) == 0 ) {
c l o s e ( t u b o [ 1 ] ) ; dup2 ( t u b o [ 0 ] , 0 ) ; c l o s e ( t u b o [ 0 ] ) ;
consumidor ( ) ;
} else {
cl os e ( tubo [ 0] ) ; c los e ( tubo [ 1] ) ;
s l e e p ( 3 ) ; k i l l ( pid2 , 1 5 ) ; wa it ( ) ; wait ( ) ;
}
}
}
}

31

Pregunta 5 (2,5 puntos) Jun 2012


Se quiere cambiar la prctica 6. Dibujo de imgenes contenidas en ficheros usando pipes, que muestra
una serie de imgenes pasadas como parmetros, para que se comporte como sigue:
Las imgenes se deben presentar separadas por intervalos de tiempo de 3 segundos. Todas las imgenes
se borrarn 5 segundos despus de que se haya mostrado la ltima.
Supngase que este programa nunca se invocor con ms de cinco parmetros desde la lnea de
comandos.

49
50

32
33
34
35

# include
# include
# include
# include
# include
# include

< u n i s t d . h>
< s t d i o . h>
< s t d l i b . h>
< s i g n a l . h>
< f c n t l . h>
< s y s / t y p e s . h>

# d e f i n e TAMANO 1024
# d e f i n e N_MAX_IMAGENES 5
void consumidor ( void ) {
e x e c l p ( " d i s p l a y " , " d i s p l a y " , 0 , NULL ) ;
p e r r o r ( " e x e c l p de d i s p l a y s e ha e j e c u t a d o mal " ) ;
}

void productor ( in t fd_entrada , in t fd_ s alid a ) {


c h a r b u f f e r [TAMANO] ;
int leidos ;
int escritos ;
w h i l e ( ( l e i d o s = r e a d ( f d _ e n t r a d a , b u f f e r , TAMANO) ) > 0 )
esc ri t os = write ( fd_salida , buffer , l ei dos );

23
24
25
26
27

34
35
36
37
38
39

}
i n t m ain ( i n t ar gc , c h a r a r g v [ ] ) {
in t pid ;
in t contador ;
i n t tubo [2 ] ;
i n t p i d s [ N_MAX_IMAGENES ] ;
in t origen ;
i f ( a r g c > N_MAX_IMAGENES ) { e x i t ( 1 ) ; }
f o r ( c o n t a d o r = 1 ; c o n t a d o r < a r g c ; c o n t a d o r ++){
pipe ( tubo ) ;
i f ( ( p i d= f o r k ( ) ) == 0 ) {
c l os e ( tubo [ 1 ] ) ;
dup2 ( t u b o [ 0 ] , 0 ) ;
c l os e ( tubo [ 0 ] ) ;
consumidor ( ) ;
} else {
p i d s [ c o n t a d o r 1 ] = p i d ; / / E l p r i m e r v a l o r de c o n t a d o r e s 1
o r i g e n = ope n ( a r g v [ c o n t a d o r ] , O_RDONLY ) ;
c l os e ( tubo [ 0 ] ) ;
productor ( origen , tubo [ 1 ] ) ;
c l os e ( or ige n ) ; c l os e ( tubo [ 1 ] ) ;
sleep (3);
}
}

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

/ / E s p e r o 5 s e g u n d o s y mato a t o d o s
sleep (5);
f o r ( c o n t a d o r = 0 ; c o n t a d o r < N_MAX_IMAGENES ; c o n t a d o r ++){
i f ( pids [ contador ] > 0) {
k i l l ( p i d s [ c o n t a d o r ] , SIGTERM ) ;
wait ( ) ;
}
}
exit (0);

35
36
37
38
39
40
41
42
43
44

32

Prctica 7. Productor/Consumidor con semforos


Especificacin
Se adjunta a continuacin el programa pcsem.c. Se nos pide en primer lugar que lo
examinemos y lo comprendamos, y a continuacin nos piden lo siguiente:
Complelo, mntelo con la biblioteca de hebras (opcin de montaje -l pthread) y
ejectelo, explicando su funcionamiento y resultados. Fjese que el parmetro que se pasa a las hebras
sirve para identificarlas y para especificar un intervalo de tiempo entre acciones. Observe las llamadas
a sem_wait y sem_post, por las cuales la hebra productora indica que hay elementos que
pueden ser consumidos, y la hebra consumidora indica que hay hueco para introducir nuevos
datos. Adems se usa un tercer semforo como cerrojo para proteger las variables compartidas.
Realice adems las siguientes modificaciones al programa y prebelas, intentando explicar los
resultados:

Modifique la capacidad de la cola sincronizada (Caja) y observe lo que sucede.


Intercambie las velocidades relativas de productor y consumidor y observe y explique lo
que sucede.
Modifique el programa para que haya tres productores y tres consumidores a distintas
velocidades y observe su funcionamiento.
Qu sucedera si hubiera ms consumidores que productores?

pcsem.c
#define _REENTRANT
#include
#include
#include
#include
#include
#include

<stdio.h>
<string.h>
<errno.h>
<pthread.h>
<semaphore.h>
<stdlib.h>

#define Longitud 5 /* Longitud del buffer de intercambio de


datos */
#define Cantidad 20 /* Nmero de enteros totales generados
por el productor, y consumidos por el
consumidor */
/* Inicializacin de variables */
int Caja[Longitud];

/ *se crea el array-buffer */

unsigned Primero = 0; /* Entero sin signo: Primero */ unsigned


Ultimo = 0; /* Entero sin signo: Segundo */ sem_t hayelementos;
sem_t hayhuecos;
/* Estos dos semforos son
necesarios para que haya sincronizacin
condicional */
sem_t cerrojo;

/* Semforo necesario para que se


cumpla la exclusin mutua*/

/* Pone en entero en la cola, detenindose si no cabe */


void Pon(int X) {
sem_wait(&hayhuecos); /* Si el valor del semforo- contador es 0,
se bloquea, si es mayor que cero, decrementa el valor del contador, y
contina */

33

sem_wait(&cerrojo); /*Si el valor del semforo-contador es 0 se


bloquea (quiere decir que ha intentado entrar en la RC que comparten
Pon y Toma, y ahora toma est en esa regin ejecutantdo). Si el
valor del semforo es mayor que cero, lo decrementa y contina */

Caja[Ultimo] = X;
Ultimo = (Ultimo + 1) % Longitud; /*Con estas dos instrucciones se
aade un elemento al buffer */
sem_post(&cerrojo); /* Se libera el cerrojo (Toma ya puede entrar
en la RC si estaba esperando a hacerlo), para ello, se incrementa
el contador del semforo */
sem_post(&hayelementos); /* Avisa de que hay elementos en el buffer
que puede recoger Toma *. El contador del semforo de incrementa, y
como consecuencia, puede comenzar a ejecutarse el cdigo que
estuviera en sem_wait(&hayelementos) */
return;
}
/* Toma un entero de la cola, detenindose si no hay */
void Toma(int *pX) { /* El argumento es un puntero al destino
(posicin del buffer), del que tenemos que tomar un elemento*/
sem_wait(&hayelementos); /* Si hay elementos en el buffer,
decrementa el contador-semforo, y contina. Si no los hay, se queda
esperando que los haya: espera un sem_post(&hayelementos) */
sem_wait(&cerrojo); /* Si es 0 se bloquea, ya que Pon estar
ejecutando en la RC que comparten. Si es mayor que cero, se aduea del
cerrojo y contina la ejecucin */
*pX = Caja[Primero];
Primero = (Primero + 1) % Longitud; /* Estas dos instrucciones
indican que se coge (toma) un elemento del buffer */
sem_post(&cerrojo); /* Se libera el cerrojo */
sem_post(&hayhuecos); /*Se avisa de que hay huecos para poder meter
ms elementos, es decir, avisa a Pon para que pueda continuar si
est en sem_wait(&hayhuecos) */

return;
}
/* Proceso que produce enteros y los encola */
void *Productor (void *pId) { /* *Productor es un puntero a
subrutina*/
unsigned Id = *(unsigned*)pId; /* pId contiene la direccin de
Id.Productor */
unsigned long Periodo = Id * 1000;
int i;
for (i = 1; i <= Cantidad; i++) { Pon(i +
Id);
printf("---- Productor %6d produce %6d\n", Id,
i+Id);

34

/* Produce 20 enteros que mete en el buffer y cuando lo hace, sale


por pantalla: ----Productor id produce id+i */
usleep(Periodo); /* Despus de ejecutar lo anterior, suspende
la ejecucin durante Periodo microsegundos, es decir, durante
Id*1000 microsegundos */
}
return NULL;
}
/* Proceso que consume enteros */
void *Consumidor(void *pId) { unsigned Id
= *(unsigned*)pId; unsigned long Periodo
= Id * 1000; int Dato;
int j;
for (j = 1; j <= Cantidad; j++) {
Toma(&Dato);
printf("**** Consumidor %6d consume %6d\n", Id, Dato);
usleep(Periodo);
}
/* Toma (o coge) los 20 enteros del buffer, parando cada vez
durante Id*1000 microsegundos imprimiendo por pantalla: ****
Consumidor id consume id+i */
return NULL;
}
int main(void) { / *Mtodo main */
pthread_t productorid;
pthread_t consumidorid; /* Define las hebras productorid
y consumidorid */
unsigned Id_Productor=1000;
unsigned Id_Consumidor=5000; /* Se define el periodo de cada uno,
por eso, en este caso, el productor va a ser 5 veces ms rpido que
el consumidor */
/* preparar los semaforos */
sem_init(&hayelementos, 0, 0); /* Al principio no hay ningn
elemento en el buffer, por tanto, este semforo se inicializa a
0*/
sem_init(&hayhuecos, 0, Longitud);/* Al principio hay Longitud
huecos en el buffer, por tanto, este semforo se inicializa a
Longitud */
sem_init(&cerrojo, 0, 1); /* Al principio, nadie ha entrado en la
RC de Pon y Toma, por tanto, este semforo se inicializa a 1 lo que
quiere decir, que est libre el acceso a la RC */
/* crear las hebras */
pthread_create(&productorid, NULL,
Productor, &Id_Productor);
pthread_create(&consumidorid, NULL,
Consumidor, &Id_Consumidor);
/* Se crean dos hebras, productor y consumidor: el primer
argumento es la direccin donde almacenamos la id de la hebra, el
segundo es un atributo casi siempre NULL, el tercero es la
direccin donde comienza el cdigo que la hebra tiene que

35

ejecutar, y el cuarto, es la direccin de los parmetros de la


subrutina a ejecutar */

/* esperar a que acaben las hebras, en este caso no se guarda el


estado de la finalizacin */
pthread_join(productorid, NULL);
pthread_join(consumidorid, NULL);
exit(0);
}
Una vez comprendido el programa, adjuntamos el resultado de la primera ejecucin:
./pcsem
---- Productor

1000 produce

1001

**** Consumidor

5000 consume

1001

---- Productor

1000 produce

1002

---- Productor

1000 produce

1003

---- Productor

1000 produce

1004

---- Productor

1000 produce

1005

**** Consumidor

5000 consume

1002

---- Productor

1000 produce

1006

---- Productor

1000 produce

1007

**** Consumidor

5000 consume

1003

---- Productor

1000 produce

1008

**** Consumidor

5000 consume

1004

---- Productor

1000 produce

1009

**** Consumidor

5000 consume

1005

---- Productor

1000 produce

1010

**** Consumidor

5000 consume

1006

---- Productor

1000 produce

1011

**** Consumidor

5000 consume

1007

---- Productor

1000 produce

1012

**** Consumidor

5000 consume

1008

---- Productor

1000 produce

1013

**** Consumidor

5000 consume

1009

---- Productor

1000 produce

1014

**** Consumidor

5000 consume

1010

---- Productor

1000 produce

1015

**** Consumidor

5000 consume

1011

---- Productor

1000 produce

1016

**** Consumidor

5000 consume

1012

---- Productor

1000 produce

1017

**** Consumidor

5000 consume

1013

---- Productor

1000 produce

1018

**** Consumidor

5000 consume

1014

---- Productor

1000 produce

1019

**** Consumidor

5000 consume

1015

---- Productor

1000 produce

1020

**** Consumidor

5000 consume

1016

**** Consumidor

5000 consume

1017

36

**** Consumidor

5000 consume

1018

**** Consumidor

5000 consume

1019

**** Consumidor

5000 consume

1020

Como podemos observar, el productor va ms rpido que el consumidor (ver


identificadores), por tanto, es normal que al principio el productor ejecute ms veces, pero una vez
se ha llenado la caja (buffer de 5 elemntos), el productor debe quedarse esperando a que
haya huecos para seguir llenndolos. Habr un hueco cada vez que el consumidor lea un
elemento, y por tanto, se desbloquear al productor, el cual introducir un elemento y se
volver a bloquear. Este proceso seguir as hasta que el productor introduzca sus 20
elementos en la caja. Una vez que el productor termine, al consumidor le quedarn algunos
elementos en la caja que tendr que recoger a la velocidad fijada por su identificador, y as,
hasta que recoja los 20 elementos.
A continuacin se adjunta el resultado de la ejecucin con cada una de las
modificaciones que se nos piden en el enunciado:
51

con una caja de 20 elementos

./csem20
---- Productor

1000 produce

1001

**** Consumidor

5000 consume

1001

---- Productor

1000 produce

1002

---- Productor

1000 produce

1003

---- Productor

1000 produce

1004

---- Productor

1000 produce

1005

**** Consumidor

5000 consume

1002

---- Productor

1000 produce

1006

---- Productor

1000 produce

1007

---- Productor

1000 produce

1008

---- Productor

1000 produce

1009

---- Productor

1000 produce

1010

**** Consumidor

5000 consume

1003

---- Productor

1000 produce

1011

---- Productor

1000 produce

1012

---- Productor

1000 produce

1013

---- Productor

1000 produce

1014

---- Productor

1000 produce

1015

**** Consumidor

5000 consume

1004

---- Productor

1000 produce

1016

---- Productor

1000 produce

1017

---- Productor

1000 produce

1018

---- Productor

1000 produce

1019

---- Productor

1000 produce

1020

**** Consumidor

5000 consume

1005

**** Consumidor

5000 consume

1006

**** Consumidor

5000 consume

1007

**** Consumidor

5000 consume

1008

**** Consumidor

5000 consume

1009

**** Consumidor

5000 consume

1010

**** Consumidor

5000 consume

1011

**** Consumidor

5000 consume

1012

37

**** Consumidor

5000 consume

1013

**** Consumidor

5000 consume

1014

**** Consumidor

5000 consume

1015

**** Consumidor

5000 consume

1016

**** Consumidor

5000 consume

1017

**** Consumidor

5000 consume

1018

**** Consumidor

5000 consume

1019

**** Consumidor

5000 consume

1020

En este caso, la caja nunca se llega a llenar, y como el productor es ms rpido


que el consumidor (5 veces ms rpido) se ejecuta ms veces, por lo que termina mucho antes.
Adems, el consumidor no se queda nunca bloqueado por falta de elementos. Observamos pues,
que la ejecucin en este caso es ms rpida.
52

con una caja de un elemento

./csem1
---- Productor

1000 produce

1001

**** Consumidor

5000 consume

1001

---- Productor

1000 produce

1002

**** Consumidor

5000 consume

1002

---- Productor

1000 produce

1003

**** Consumidor

5000 consume

1003

---- Productor

1000 produce

1004

**** Consumidor

5000 consume

1004

---- Productor

1000 produce

1005

**** Consumidor

5000 consume

1005

---- Productor

1000 produce

1006

**** Consumidor

5000 consume

1006

---- Productor

1000 produce

1007

**** Consumidor

5000 consume

1007

---- Productor

1000 produce

1008

**** Consumidor

5000 consume

1008

---- Productor

1000 produce

1009

**** Consumidor

5000 consume

1009

---- Productor

1000 produce

1010

**** Consumidor

5000 consume

1010

---- Productor

1000 produce

1011

**** Consumidor

5000 consume

1011

---- Productor

1000 produce

1012

**** Consumidor

5000 consume

1012

---- Productor

1000 produce

1013

**** Consumidor

5000 consume

1013

---- Productor

1000 produce

1014

**** Consumidor

5000 consume

1014

---- Productor

1000 produce

1015

**** Consumidor

5000 consume

1015

---- Productor

1000 produce

1016

**** Consumidor

5000 consume

1016

---- Productor

1000 produce

1017

**** Consumidor

5000 consume

1017

38

---- Productor

1000 produce

1018

**** Consumidor

5000 consume

1018

---- Productor

1000 produce

1019

**** Consumidor

5000 consume

1019

---- Productor

1000 produce

1020

**** Consumidor

5000 consume

1020

En este caso, la caja se llena con cada elemento que introduce en ella el productor,
por lo que ste est durante cada ejecucin bloqueado esperando a que haya huecos, de modo
que slo puede producir cuando el consumidor lee un dato de la caja y lo desbloquea. Esta
ejecucin es muy lenta.
53

con una caja de 5 elementos (como al principio) pero con las velocidades relativas al
productor y al consumidor cambiadas (basta intercambiar los valores de
Id_Productor e Id_Consumidor.

./csemConsumidor
---- Productor

5000 produce

5001

**** Consumidor

1000 consume

5001

---- Productor

5000 produce

5002

**** Consumidor

1000 consume

5002

---- Productor

5000 produce

5003

**** Consumidor

1000 consume

5003

---- Productor

5000 produce

5004

**** Consumidor

1000 consume

5004

---- Productor

5000 produce

5005

**** Consumidor

1000 consume

5005

---- Productor

5000 produce

5006

**** Consumidor

1000 consume

5006

---- Productor

5000 produce

5007

**** Consumidor

1000 consume

5007

---- Productor

5000 produce

5008

**** Consumidor

1000 consume

5008

---- Productor

5000 produce

5009

**** Consumidor

1000 consume

5009

---- Productor

5000 produce

5010

**** Consumidor

1000 consume

5010

---- Productor

5000 produce

5011

**** Consumidor

1000 consume

5011

---- Productor

5000 produce

5012

**** Consumidor

1000 consume

5012

---- Productor

5000 produce

5013

**** Consumidor

1000 consume

5013

---- Productor

5000 produce

5014

**** Consumidor

1000 consume

5014

---

5000 produce

5015

**** Consumidor

Productor

1000 consume

5015

---- Productor

5000 produce

5016

**** Consumidor

1000 consume

5016

---- Productor

5000 produce

5017

**** Consumidor

1000 consume

5017

39

---- Productor

5000 produce

5018

**** Consumidor

1000 consume

5018

---- Productor

5000 produce

5019

**** Consumidor

1000 consume

5019

---- Productor

5000 produce

5020

**** Consumidor

1000 consume

5020

En este caso, podemos observar que es el consumidor el que se queda bloqueado


esperando que haya elementos producidos para sacarlos de la caja, ya que el productor es
cinco veces ms lento que l. Esta ejecucin, por tanto, tambin es lenta.
54

Lo mismo con tres productores y tres con tres consumidores (ms rpidos los
productores que los consumidores, y eligiendo un tamao de caja de 5 elementos,
como al principio)

En este caso, hay que modificar el mtodo main, que quedara como se puede observar a
continuacin:
int main(void) {
pthread_t
productorid1;
pthread_t
productorid2;
pthread_t productorid3;
pthread_t
consumidorid1;
pthread_t
consumidorid2;
pthread_t consumidorid3;
unsigned
Id_Productor1=1000;
unsigned
Id_Productor2=2000;
unsigned Id_Productor3=3000;
unsigned
Id_Consumidor1=5000;
unsigned
Id_Consumidor2=7000;
unsigned Id_Consumidor3=8000;
/* preparar los semaforos */
sem_init(&hayelementos, 0, 0);
sem_init(&hayhuecos, 0, Longitud);
sem_init(&cerrojo, 0, 1);
/* crear las hebras */
pthread_create(&productorid1, NULL,
Productor, &Id_Productor1);
pthread_create(&productorid2, NULL,
Productor, &Id_Productor2);
pthread_create(&productorid3, NULL,
Productor, &Id_Productor3);
pthread_create(&consumidorid1, NULL,
Consumidor, &Id_Consumidor1);
pthread_create(&consumidorid2, NULL,
Consumidor, &Id_Consumidor2);
pthread_create(&consumidorid3, NULL,
Consumidor, &Id_Consumidor3);
/* esperar a que acaben las hebras */ pthread_join(productorid1,
NULL); pthread_join(productorid2, NULL);
pthread_join(productorid3, NULL);

40

pthread_join(consumidorid1,
NULL);
pthread_join(consumidorid2,
NULL);
pthread_join(consumidorid3, NULL);
exit(0);
}
El resultado de la ejecucin en este caso, es el siguiente:
./csem3y3
---- Productor

1000 produce

1001

---- Productor

2000 produce

2001

---- Productor

3000 produce

3001

**** Consumidor

5000 consume

1001

**** Consumidor

7000 consume

2001

**** Consumidor

8000 consume

3001

---- Productor

1000 produce

1002

---- Productor

2000 produce

2002

---- Productor

1000 produce

1003

---- Productor

3000 produce

3002

---- Productor

1000 produce

1004

**** Consumidor

5000 consume

1002

---- Productor

2000 produce

2003

**** Consumidor

7000 consume

2002

---- Productor

1000 produce

1005

**** Consumidor

8000 consume

1003

---- Productor

1000 produce

1006

**** Consumidor

5000 consume

3002

---- Productor

2000 produce

2004

**** Consumidor

7000 consume

1004

---- Productor

3000 produce

3003

**** Consumidor

5000 consume

2003

---- Productor

1000 produce

1007

**** Consumidor

8000 consume

1005

---- Productor

2000 produce

2005

**** Consumidor

5000 consume

1006

---- Productor

1000 produce

1008

**** Consumidor

7000 consume

2004

---- Productor

3000 produce

3004

**** Consumidor

8000 consume

3003

---- Productor

2000 produce

2006

**** Consumidor

5000 consume

1007

---- Productor

1000 produce

1009

**** Consumidor

7000 consume

2005

---- Productor

3000 produce

3005

**** Consumidor

5000 consume

1008

---- Productor

1000 produce

1010

**** Consumidor

8000 consume

3004

---- Productor

2000 produce

2007

**** Consumidor

7000 consume

2006

41

---- Productor

3000 produce

3006

**** Consumidor

5000 consume

1009

---- Productor

1000 produce

1011

**** Consumidor

5000 consume

3005

---- Productor

2000 produce

2008

**** Consumidor

8000 consume

1010

---- Productor

1000 produce

1012

**** Consumidor

7000 consume

2007

---- Productor

3000 produce

3007

**** Consumidor

5000 consume

3006

---- Productor

1000 produce

1013

**** Consumidor

8000 consume

1011

---- Productor

2000 produce

2009

**** Consumidor

7000 consume

2008

---- Productor

3000 produce

3008

**** Consumidor

5000 consume

1012

---- Productor

1000 produce

1014

**** Consumidor

5000 consume

3007

---- Productor

2000 produce

2010

---- Productor

1000 produce

1015

**** Consumidor

7000 consume

1013

**** Consumidor

8000 consume

2009

---- Productor

3000 produce

3009

**** Consumidor

5000 consume

3008

---- Productor

1000 produce

1016

---- Productor

2000 produce

2011

**** Consumidor

7000 consume

1014

---- Productor

3000 produce

3010

**** Consumidor

8000 consume

2010

**** Consumidor

5000 consume

1015

---- Productor

1000 produce

1017

---- Productor

2000 produce

2012

**** Consumidor

7000 consume

3009

**** Consumidor

5000 consume

1016

---- Productor

1000 produce

1018

---- Productor

3000 produce

3011

**** Consumidor

8000 consume

2011

**** Consumidor

5000 consume

3010

---- Productor

1000 produce

1019

---- Productor

2000 produce

2013

**** Consumidor

7000 consume

1017

**** Consumidor

5000 consume

2012

---- Productor

3000 produce

3012

**** Consumidor

8000 consume

1018

---- Productor

1000 produce

1020

---- Productor

2000 produce

2014

**** Consumidor

7000 consume

3011

**** Consumidor

5000 consume

1019

---- Productor

3000 produce

3013

---- Productor

2000 produce

2015

42

**** Consumidor

8000 consume

2013

**** Consumidor

5000 consume

3012

---- Productor

3000 produce

3014

---- Productor

2000 produce

2016

**** Consumidor

7000 consume

1020

**** Consumidor

5000 consume

2014

---- Productor

2000 produce

2017

---- Productor

3000 produce

3015

**** Consumidor

8000 consume

3013

---- Productor

2000 produce

2018

**** Consumidor

7000 consume

2015

---- Productor

3000 produce

3016

**** Consumidor

8000 consume

3014

---- Productor

2000 produce

2019

**** Consumidor

7000 consume

2016

---- Productor

2000 produce

2020

**** Consumidor

7000 consume

2017

---- Productor

3000 produce

3017

**** Consumidor

8000 consume

3015

**** Consumidor

7000 consume

2018

---- Productor

3000 produce

3018

**** Consumidor

8000 consume

3016

---- Productor

3000 produce

3019

**** Consumidor

7000 consume

2019

---- Productor

3000 produce

3020

**** Consumidor

8000 consume

2020

**** Consumidor

7000 consume

3017

**** Consumidor

8000 consume

3018

**** Consumidor

8000 consume

3019

**** Consumidor

8000 consume

3020

En la ejecucin se observa que en cuanto la caja se llena, los productores se bloquean y


al hacer sem_post(&hayhuecos) se despierta el productor que estuviese ms tiempo esperando,
ya que se implementa mediante una cola FIFO. Tambin los consumidores se quedan bloqueados
e igualmente, cuando un productor haga sem_post(&hayelementos), se desbloquear el
consumidor que lleve ms tiempo esperando.
55

Lo mismo con tres productores y dos consumidores. Qu sucede?

./cem3y2
---- Productor

1000 produce

1001

---- Productor

2000 produce

2001

---- Productor

3000 produce

3001

**** Consumidor

5000 consume

1001

**** Consumidor

7000 consume

2001

---- Productor

1000 produce

1002

---- Productor

2000 produce

2002

---- Productor

1000 produce

1003

---- Productor

3000 produce

3002

**** Consumidor

5000 consume

3001

---- Productor

1000 produce

1004

43

**** Consumidor

7000 consume

1002

---- Productor

2000 produce

2003

**** Consumidor

5000 consume

2002

---- Productor

3000 produce

3003

**** Consumidor

7000 consume

1003

---- Productor

1000 produce

1005

**** Consumidor

5000 consume

3002

---- Productor

2000 produce

2004

**** Consumidor

5000 consume

1004

---- Productor

3000 produce

3004

**** Consumidor

7000 consume

2003

---- Productor

1000 produce

1006

**** Consumidor

5000 consume

3003

---- Productor

2000 produce

2005

**** Consumidor

7000 consume

1005

---- Productor

1000 produce

1007

**** Consumidor

5000 consume

2004

---- Productor

3000 produce

3005

**** Consumidor

5000 consume

3004

---- Productor

2000 produce

2006

**** Consumidor

7000 consume

1006

---- Productor

1000 produce

1008

**** Consumidor

5000 consume

2005

---- Productor

3000 produce

3006

**** Consumidor

7000 consume

1007

---- Productor

1000 produce

1009

**** Consumidor

5000 consume

3005

---- Productor

2000 produce

2007

**** Consumidor

7000 consume

2006

---- Productor

3000 produce

3007

**** Consumidor

5000 consume

1008

---- Productor

1000 produce

1010

**** Consumidor

5000 consume

3006

---- Productor

2000 produce

2008

**** Consumidor

7000 consume

1009

---- Productor

1000 produce

1011

**** Consumidor

5000 consume

2007

---- Productor

3000 produce

3008

**** Consumidor

7000 consume

3007

---- Productor

2000 produce

2009

**** Consumidor

5000 consume

1010

---- Productor

1000 produce

1012

**** Consumidor

5000 consume

2008

---- Productor

3000 produce

3009

---- Productor

2000 produce

2010

**** Consumidor

7000 consume

1011

**** Consumidor

5000 consume

3008

---- Productor

1000 produce

1013

---- Productor

2000 produce

2011

**** Consumidor

7000 consume

2009

44

**** Consumidor

5000 consume

1012

---- Productor

3000 produce

3010

**** Consumidor

7000 consume

3009

---- Productor

1000 produce

1014

**** Consumidor

5000 consume

2010

---- Productor

2000 produce

2012

**** Consumidor

5000 consume

1013

---- Productor

3000 produce

3011

**** Consumidor

7000 consume

2011

---- Productor

1000 produce

1015

**** Consumidor

5000 consume

3010

---- Productor

2000 produce

2013

**** Consumidor

7000 consume

1014

---- Productor

1000 produce

1016

**** Consumidor

7000 consume

2012

---- Productor

3000 produce

3012

**** Consumidor

7000 consume

3011

---- Productor

2000 produce

2014

**** Consumidor

7000 consume

1015

---- Productor

1000 produce

1017

**** Consumidor

7000 consume

2013

---- Productor

3000 produce

3013

**** Consumidor

7000 consume

1016

---- Productor

2000 produce

2015

En este caso el programa no terminar nunca, ya que, aunque al principio es igual que
los dems, y la caja se llena y se van bloqueando y despertando las hebras, la diferencia est en que
una vez que los consumidores han ledo o consumido sus 20 elementos, acabarn, pero los
productores no habrn terminado, y en cuanto se
llene la caja, se quedarn esperando a que haya huecos, pero como los
consumidores ya han terminado, no los podrn desbloquear con sem_post(&hayhuecos). En este
caso, los productores se quedarn bloqueados indefinidamente, y como se ha dicho antes, el programa
no acabar.
NOTA FINAL:
Notar que en caso de tener slo un productor y un consumidor, no hara falta
usar el semforo cerrojo, ya que la nica variable que usan Pon y Toma es caja,
pero manejan NDICES DISJUNTOS.

45

Pregunta 3 (2,5 puntos) Jun 2009


Subpregunta 3.1 (1 punto)
Si en la prctica 6 cambiamos las definiciones de Longitud y Cantidad por las siguientes:
# d e f i n e L ongi t ud 2
# define Cantidad 4

Escriba una traza de salida posible, explicndola y anotando cada lnea con el nmero de
segundo en que tiene lugar, comenzando por 0 (momento inicial).
Momento
0
0

Mensaje
****

Productor
Consumidor

1000
5000

produce
consume

1001
1001

1
2
5

****

Productor
Productor
Consumidor

1000
1000
5000

produce
produce
consume

1002
1003
1002

Productor

1000

produce

1004

10

****

Consumidor

5000

consume

1003

15

****

Consumidor

5000

consume

1004

Explicacin
Comienza y pone un dato.
Comienza y lo recoge inmediatamente.
Pone siguiente dato.
Y el siguente. Ya no caben ms.
En el quinto segundo se consume
un elemento.
Inmediatamente el productor pone
lo que tena bloqueado y termina.
El consumidor saca a su ritno lo
que hay.
Idem.

Subpregunta 3.2 (1 punto)


Repita el apartado anterior si adems cambiamos los identificadores a stos:
unsigned Id_Productor =
5000; un si g ne d Id_Consu
midor =1000;

Momento
0
0

Mensaje
****

Productor
Consumidor

5000
1000

produce
consume

5001
5001

Productor

5000

produce

5002

5
10

****
-

Consumidor
Productor

1000
5000

consume
produce

5002
5003

10
15
15

****
****

Consumidor
Productor
Consumidor

1000
5000
1000

consume
produce
consume

5003
5004
5004

Explicacin
Comienza y pone un dato.
Comienza y lo recoge inmediatamente.
Pone siguiente dato al cabo de 5 segundos. El consumidor lleva esperando 4 segundos.
Inmediatamente lo consume.
Y todo igual: lo que se produce se
consume inmediatamente. La cola
casi siempre est vaca.

Subpregunta 3.3 (0,5 puntos)


Explique las diferencias que tienen lugar si en lugar de hacer los cambios anteriores en la
prctica 6, los hacemos en la prctica 7.
No hay diferencias, ya que la cola que se implementa tiene la misma semntica.

46

Prctica 8. Productor/Consumidor con variables condicin y cerrojos


Especificacin
Nos piden examinar el programa pcvc.c, que se adjunta a continuacin, para despus
llevar a cabo las siguientes instrucciones:
Compile el programa, mntelo con la biblioteca de hebras y ejectelo, explicando su
funcionamiento y resultados. Comprelo con la versin que utiliza semforos, pcsem/pcsem.c.
Observe que la llamada a pthread_cond_signal no contina inmediatamente a la hebra elegida
(si la hay), sino que slamente la activa, por lo que no se garantiza que la condicin sealada se cumpla,
por lo que ha de reevaluarse despus del pthread_cond_wait. Adems esta ltima operacin debe
cerrar atmicamente la zona crtica donde va a trabajar, por lo que lleva asociado un cerrojo. Ahora,
para proteger las variables compartidas utilizamos un cerrojo en lugar de un semforo, que es
adems el que se asocia a la variable condicin. Compruebe que el comportamiento es similar a la
realizacin con semforos.
pcvc.c
#define _REENTRANT
#include
#include
#include
#include

<stdio.h>
<stdlib.h>
<pthread.h>
<stdlib.h>

#define Longitud 5
#define Cantidad 20
int Caja[Longitud];
unsigned Primero = 0;
unsigned Ultimo = 0;
unsigned Numero = 0;
pthread_mutex_t cerrojo = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t huecos = PTHREAD_COND_INITIALIZER;
pthread_cond_t elementos = PTHREAD_COND_INITIALIZER;
/* Pone en entero en la cola, detenindose si no cabe */
void Pon(int X) {
pthread_mutex_lock (&cerrojo); /* Cogemos el cerrojo, ahora
Toma no puede entrar a esta RC, hasta que soltemos o
abramos el cerrojo */
while (Numero == Longitud)
pthread_cond_wait(&huecos, &cerrojo); /* Mientras el Numero
de elementos en el buffer sea igual a la Longitud del mismo, no se
puede continuar. Se comprueba la condicin: como no hay huecos,
liberamos el cerrojo automticamente, mientras esperamos a que los
haya (signal(&huecos)).Cuando esto ocurra, la hebra se activa, pero
no contina inmediatamente, se ejecuta el while para volver a
comprobar la condicin, y si se cumple, se contina */
Caja[Ultimo] = X;
Ultimo = (Ultimo + 1) % Longitud;
Numero++; /* Se introduce un nuevo elemento en el buffer, y se
aumenta el contador Numero que es el que lleva la cuenta de cuntos
elementos hay en el buffer */

47

pthread_cond_signal(&elementos); /* Se avisa de que ya hay


elementos en el buffer para poder sacarlos */
pthread_mutex_unlock(&cerrojo); /* Se suelta el cerrojo
*/
return;
}
/* Toma un entero de la cola, detenindose si no hay */
void Toma(int *pX) {
pthread_mutex_lock (&cerrojo); /* Cogemos o cerramos el cerrojo */
while (Numero == 0)
pthread_cond_wait(&elementos, &cerrojo); /* Mientras el
buffer est vaco, no podemos continuar. Se comprueba la condicin, y
si no hay elementos en el buffer, se suelta inmediatamente el
cerrojo, y nos quedamos esperando un signal(&elementos). Cuando se
nos lance este aviso, la hebra se activa, pero while deber comprobar
de nuevo la condicin para que se contine la ejecucin (ya que la
condicin puede haber cambiado) */

*pX = Caja[Primero];
Primero = (Primero + 1) % Longitud;
Numero--; /* Sacamos un elemento del buffer y decrementamos
Numero, la cuenta de los elementos que hay dentro del buffer */
pthread_cond_signal(&huecos); /* Se avisa de que ya hay huecos en
el buffer para poder seguir llenndolos */
pthread_mutex_unlock(&cerrojo); /* Se suelta el cerrojo
*/
return;
}
/* Proceso que produce enteros y los encola */
void *Productor (void *pId) { unsigned Id
= *(unsigned*)pId; unsigned long Periodo
= Id * 1000; int i;
for (i = 1; i <= Cantidad; i++) { Pon(i +
Id);
printf("---- Productor %6d produce %6d\n", Id, i+Id);
usleep(Periodo);
}
return NULL;
}
/* Proceso que consume enteros */
void *Consumidor(void *pId) { unsigned Id
= *(unsigned*)pId; unsigned long Periodo
= Id * 1000; int Dato;
int j;

48

for (j = 1; j <= Cantidad; j++) {


Toma(&Dato);
printf("**** Consumidor %6d consume %6d\n", Id, Dato);
usleep(Periodo);
}
return NULL;
}
int main(void) { pthread_t
productorid;
pthread_t consumidorid;
unsigned Id_Productor=1000;
unsigned Id_Consumidor=5000;
/* crear las hebras */
pthread_create(&productorid, NULL, Productor,
&Id_Productor);
pthread_create(&consumidorid, NULL, Consumidor,
&Id_Consumidor);
/* esperar a que acaben las hebras */
pthread_join(productorid, NULL);
pthread_join(consumidorid, NULL); exit(0);
}
A continuacin se adjunta el resultado de la ejecucin de este programa:
./pcvc
---- Productor

1000 produce

1001

**** Consumidor

5000 consume

1001

---- Productor

1000 produce

1002

---- Productor

1000 produce

1003

---- Productor

1000 produce

1004

---- Productor

1000 produce

1005

**** Consumidor

5000 consume

1002

---- Productor

1000 produce

1006

---- Productor

1000 produce

1007

**** Consumidor

5000 consume

1003

---- Productor

1000 produce

1008

**** Consumidor

5000 consume

1004

---- Productor

1000 produce

1009

**** Consumidor

5000 consume

1005

---- Productor

1000 produce

1010

**** Consumidor

5000 consume

1006

---- Productor

1000 produce

1011

**** Consumidor

5000 consume

1007

---- Productor

1000 produce

1012

**** Consumidor

5000 consume

1008

---- Productor

1000 produce

1013

**** Consumidor

5000 consume

1009

---- Productor

1000 produce

1014

**** Consumidor

5000 consume

1010

49

---- Productor

1000 produce

1015

**** Consumidor

5000 consume

1011

---- Productor

1000 produce

1016

**** Consumidor

5000 consume

1012

---- Productor

1000 produce

1017

**** Consumidor

5000 consume

1013

---- Productor

1000 produce

1018

**** Consumidor

5000 consume

1014

---- Productor

1000 produce

1019

**** Consumidor

5000 consume

1015

----

Productor

1000 produce

1020

**** Consumidor

5000 consume

1016

**** Consumidor

5000 consume

1017

**** Consumidor

5000 consume

1018

**** Consumidor

5000 consume

1019

**** Consumidor

5000 consume

1020

Como podemos observar, este programa hace lo mismo que el de la prctica


5 (semforos), pero con diferencias en la manera de implementarlo, ya que utiliza cerrojos y
variables de condicin. Para utilizar correctamente las variables de condicin, hay que tener en
cuenta que:
Asocian la condicin de espera al cerrojo, abriendo el cerrojo si hay que esperar.
Al sealar la condicin no se garantiza quin contina, no se suelta el cerrojo (hay que
hacerlo despus), la hebra elegida slo se activa (no contina inmediatamente), hay que
volver a comprobar el valor de la condicin, ya
que pueden haberse ejecutado instrucciones que lo hayan cambiado.

50

Pregunta 4 Jun 2010


En este ejercicio se debe modificar la prctica 8: Productor/consumidor con variables condicin y
cerrojos.

Subpregunta 4.1 (0,5 puntos)


Modificar ese programa para que se ejecuten 10 productores y 10 consumidores. los
pasan
a
los
productores
sern:
identificadores
que
se
1000,1100,1200,1300,1400,1500,1600,1700,1800,1900. Los de los consumidores sern
5000,5100,5200,5300,5400,5500,5600,5700,5800,5900.
La hebra que ejecuta el procedimiento main deber esperar a que finalicen todas las hebras creadas.

Subpregunta 4.2 (1,5 puntos)


Se quiere modificar el programa para que los productores amplien el tamao de Caja cuando estn
bloqueados, al menos, la mitad de los productores esperando hueco.
Para adaptar el cdigo original a esta especificacin, se debe cambiar la declaracin de la variable
Caja, para poder redimensionar su tamao. Esa variable ahora se inicializa al principio de la funcin
main con la sentencia:
Caja=malloc(Longitud_Inicial*sizeof(int))

En el resto del cdigo se sigue empleando Caja igual que antes. Tambin se debe cambiar la
declaracin de
Longitud, que ya no es una constante y se declara ahora como variable.
Se pide modificar el cdigo de la funcin Pon para que duplique el tamao de Caja, cuando el
nmero de productores bloqueados esperando hueco sea la mitad de los productores creados.
Se proporciona el procedimiento duplicaCaja, que duplica el tamao de Caja. A continuacin
se muestra el cdigo de este procedimiento para ilustrar cmo se podra realizar. No es necesario
realizar modificacin alguna del mismo.
1

# define Lo n gi t u d _ In i ci a l 5

unsigned L o ng it ud = L o n g i t u d _ I n i c i a l ;
int Caja ;
5

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

void d u p l i c a C a j a ( ) {
int dest ;
int i , j , copias ;
d e s t = m a l l o c ( ( L o n g i t u d 2) s i z e o f ( i n t ) ) ;
j =Pri mero ;
c o p i a s =0;
f o r ( i = P r i m e r o ; c o p i a s < Numero ; i = ( i + 1 ) % L o n g i t u d ) {
d e s t [ j ]= C a j a [ i ] ;
j = ( j +1) %( L o n g i t u d 2 ) ;
c o p i a s ++;
}
U l t i mo = j ;
Caja= d e s t ;
Longitud = Longitud 2;
}
# d e f i n e _REENTRANT

2
36
37
38
39

# include
# include
# include
# include

< s t d i o . h>
< s t d l i b . h>
< p t h r e a d . h>
< s t d l i b . h>

7
8

# d e f i n e PROD_CONS 10

9
28
29

# define Longitud_Inicial 5
# d e f i n e C a n t i d a d 20

12
40
41
42
43
44
45

u n s i g n e d L o n g i t u d= L o n g i t u d _ I n i c i a l ;
i n t C aja ;
unsigne d Primero = 0;
unsigned Ultimo = 0;
u n s i g n e d Numero = 0 ;
unsigned Bloqueados = 0;

51

46
47
48

p t h r e a d _ m u t e x _ t c e r r o j o = PTHREAD_MUTEX_INITIALIZER;
p t h r e a d _ c o n d _ t h u e c o s = PTHREAD_COND_INITIALIZER;
p t h r e a d _ c o n d _ t e l e m e n t o s = PTHREAD_COND_INITIALIZER;

22
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
38
39
40
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
59
60
61
22
23
24
25
26
27
28
29
30
31
32
73
74
75
22
23
24
25
26
27
28
29
30
31
32
87
88
89
9
10
11
12

void duplicaCaja ( ) {
int dest ;
in t i , j , copias ;
d e s t = m a l l o c ( ( L o n g i t u d 2) s i z e o f ( i n t ) ) ;
j =P r i m e r o ;
c o p i a s =0;
f o r ( i =P r i m e r o ; c o p i a s < Numero ; i =( i +1) % L o n g i t u d ) {
d e s t [ j ]= C aja [ i ] ;
j =( j +1) %( L o n g i t u d 2 ) ;
c o p i a s ++;
}
U l t i m o= j ;
C aja= d e s t ;
L o n g i t u d= L o n g i t u d 2 ;
}
/ Pone e n e n t e r o e n l a c o l a , d e t e n i e n d o s e s i no c a b e /
v o i d Pon ( i n t X ) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o ) ;
w h i l e ( Numero == L o n g i t u d ) {
i f ( B l o q u e a d o s == PROD_CONS / 2 ) {
duplicaCaja ( ) ;
p t h r e a d _ c o n d _ s i g n a l (& h u e c o s ) ;
} else {
B l o q u e a d o s ++;
p t h r e a d _ c o n d _ w a i t (& h u e c o s ,& c e r r o j o ) ;
B l o q u e a d o s ;
}
}
C aja [ U l t i m o ] = X ;
Ultimo = ( Ultimo + 1) % Lo n gi tu d ;
Numero++;
p t h r e a d _ c o n d _ s i g n a l (& e l e m e n t o s ) ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o ) ;
}
/ Toma un e n t e r o de l a c o l a , d e t e n i e n d o s e s i no hay /
v o i d Toma ( i n t pX ) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o ) ;
w h i l e ( Numero == 0 )
p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s , &c e r r o j o ) ;
pX = C aja [ P r i m e r o ] ;
Primero = ( Primero + 1) % L o n g it u d ;
Numero;
p t h r e a d _ c o n d _ s i g n a l (& h u e c o s ) ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o ) ;
return ;
}
/ P r o c e s o que p r o d u c e e n t e r o s y l o s e n c o l a /
void Productor ( v oid pId ) {
u n s i g n e d I d = ( u n s i g n e d ) p I d ;
unsigned long Periodo = Id 1000;
int i ;
f o r ( i = 1 ; i <= C a n t i d a d ; i ++) {
Pon ( i + I d ) ;
p r i n t f ( " P r o d u c t o r %6d p r o d u c e %6d \ n " , Id , i +I d ) ;
u s l e e p ( Periodo ) ;
}
r e t u r n NULL ;
}
/ P r o c e s o que c ons ume e n t e r o s /
v o i d Consumidor ( v o i d pId ) {
u n s i g n e d I d = ( u n s i g n e d ) p I d ;
unsigned long Periodo = Id 1000;
i n t Dato ;
int j ;
f o r ( j = 1 ; j <= C a n t i d a d ; j ++) {
Toma(& Dato ) ;
p r i n t f ( " C o n s u m i d o r %6d c ons ume %6d \ n " , Id , Dato ) ;
u s l e e p ( Periodo ) ;
}
r e t u r n NULL ;

13
14
15
16
17
18
19
20

102
15

i n t main ( v o i d ) {

52

C aja= m a l l o c ( L o n g i t u d _ I n i c i a l s i z e o f ( i n t ) ) ;
p t h r e a d _ t p r o d u c t o r i d [PROD_CONS ] ;
p t h r e a d _ t c o n s u m i d o r i d [PROD_CONS ] ;
u n s i g n e d I d _ P r o d u c t o r =1000;
u n s i g n e d I d _ C o n s u m i d o r =5000;
int i ;
/ cr ear l a s hebras /
f o r ( i =0; i < PROD_CONS ; i ++) {
p t h r e a d _ c r e a t e (& p r o d u c t o r i d [ i ] , NULL , P r o d u c t o r , &I d _ P r o d u c t o r ) ;
p t h r e a d _ c r e a t e (& c o n s u m i d o r i d [ i ] , NULL , C ons umidor , &I d _ C o n s u m i d o r ) ;
I d _ P r o d u c t o r= I d _ P r o d u c t o r +100;
I d _ C o n s u m i d o r =I d _ C o n s u m i d o r +100;
}

16
17
18
19
20
21
22
23
24
25
26
27
28
117

/ e s p e r a r a que a c a b e n l a s h e b r a s /
f o r ( i =0; i < PROD_CONS ; i ++) {
p t h r e a d _ j o i n ( p r o d u c t o r i d [ i ] , NULL ) ;
p t h r e a d _ j o i n ( c o n s u m i d o r i d [ i ] , NULL ) ;
}
exit (0);

8
9
10
11
12
13
14

53

Pregunta 3 Jun 2012


Se ha modificado el cdigo de la prctica 8. Productor/consumidor con variables condicin y
cerrojos. Se han cambiado una serie de sentencias, quedando como resultado el cdigo que se
muestra a continuacin. Las sentencias anteriores y posteriores al fragmento siguiente permanecen sin
cambios. Las sentencias que se han modificado se han marcado con un asterisco a la izquierda del
nmero de lnea.
1: // Las sentencias anteriores no han cambiado
2: void Pon(int X, unsigned long Periodo, unsigned int Id) {
3: pthread_mutex_lock (&cerrojo);
4:
while (Numero == Longitud)
5:
pthread_cond_wait(&huecos, &cerrojo);
6:
Caja[Ultimo] = X;
7:
Ultimo = (Ultimo + 1) % Longitud;
8: Numero++;
9: pthread_cond_signal(&elementos);
printf("---- Productor %6d produce %6d\n", Id, X);
*10:
usleep(Periodo);
*11:
12: pthread_mutex_unlock(&cerrojo);
13:
return;
14: }
15: /* Toma un entero de la cola, detenindose si no hay */
16: void Toma(int *pX, unsigned long Periodo, unsigned int Id) {
17: pthread_mutex_lock (&cerrojo);
18:
while (Numero == 0)
19:
pthread_cond_wait(&elementos, &cerrojo);
20:
*pX = Caja[Primero];
21:
Primero = (Primero + 1) % Longitud;
22:
Numero--;
23: pthread_cond_signal(&huecos);
printf("**** Consumidor %6d consume %6d\n", Id, *pX);
*24:
usleep(Periodo);
*25:
26: pthread_mutex_unlock(&cerrojo);
27:
return;
28: }
29: /* Proceso que produce enteros y los
encola */ 30: void *Productor (void *pId)
{
31: unsigned Id =
*(unsigned*)pId; 32:
unsigned long Periodo = Id *
1000; 33:
int i;
34:
for (i = 1; i <= Cantidad; i++) {
Pon(i + Id,Periodo,Id);
*35:
*36:
37:
}
38:
return NULL;
39: }
40: /* Proceso que consume
enteros */ 41: void
*Consumidor(void *pId) {
42:
unsigned Id =
*(unsigned*)pId;
43:
unsigned long Periodo =
Id * 1000; 44:
int Dato;
45:
int j;
46:
for (j = 1; j <= Cantidad; j++) {
Toma(&Dato,Periodo,Id);
*47:
*48:
49:
}
50:
return NULL;
51: }
// Las sentencias siguientes no han cambiado

Indicar si la implementacin presentada de la cola sincronizada mantiene las propiedades de


exclusin mutua y sincronizacin condicional de la solucin original. Por otro lado, se ha comprobado que
esta propuesta tarda ms tiempo en ejecutarse. Justificar la causa de este retardo.
El acceso al mutex y las variables condicin se mantinen igual. La diferencia es que ahora las
senten- cias printf y usleep se ejecutan dentro del mutex que soporta el buffer. Se mantienen la
propiedades de exclusin y sincronizacin.
El programa tarda ms en ejecutarse, porque la ejecucin de los usleep se hace con el cerrojo
blo- queado, y por eso dos usleep (de productor o de consumidor) no podrn ejecutarse en
paralelo, y al
no haber apenas cdigo ejecutado fuera del cerrojo (simplemente las llamadas y retornos a Pon y
Toma) casi toda la ejecucin est serializada.

54

Subpregunta 3.1 (1 puntos)


Supngase que en la implementacin anterior se sustituyen las lneas 10, 11 y 12 por las siguientes:
pthread_mutex_unlock(&cerrojo);
pthread_mutex_lock(&cerrojo);
printf("---- Productor %6d produce
%6d\n", Id, X);
pthread_mutex_unlock(&cerrojo);
usleep(Periodo);

y las lneas 24, 25 y 26 por las siguientes:


pthread_mutex_unlock(&cerrojo);
pthread_mutex_lock (&cerrojo);
printf("**** Consumidor %6d consume %6d\n",
Id, *pX); pthread_mutex_unlock(&cerrojo);
usleep(Periodo);

Comparar la ejecucin de esta implementacin con la presentada en la subpregunta anterior, segn su


adecua- cin para su ejecucin concurrente y sus prestaciones.
Esta solucin mejora el paralelismo del programa por dos motivos:
Los usleep estn fuera del cerrojo. Eso permite que puedan ejecutarse en paralelo.
Antes de ejecutar los printf las hebras se salen del cerrojo, lo que permite que el signal que
han ejecutado tenga efecto (desbloquee a otra hebra y vuelva entrar en el mutex). Pero los printf
siguen dentro del mutex, y esto no es necesario. Adems esta solucin puede crear confusin en
las trazas si unas hebras adelantan a otras cuando sacan las trazas (la solucin original ya tenia
este problema).

Subpregunta 3.2 (0,5 puntos)


Finalmente, se han modificado en la primera implementacin de este ejercicio las sentencias de las
lneas 10, 11 y 12 por las siguientes:
pthread_mutex_unlock(&cerrojo);
printf("---- Productor %6d produce %6d\n", Id, X);
usleep(Periodo);

y las lneas 24, 25 y 26 por las siguientes:


pthread_mutex_lock (&cerrojo);
printf("**** Consumidor %6d consume %6d\n",
Id, *pX); usleep(Periodo);

Comparar esta propuesta con la de la subpregunta anterior, segn los mismos criterios expuestos en
su enun- ciado.
La diferencia entre la solucin original y la de este ejercicio es que el consumidor ha cambiado
el pthread_mutex_unlock por pthread_mutex_lock por lo dems el nico cambio es que los
printf y usleep se ejecutan antes de los retornos de Pon y Toma. Todos los datos de los printf y
usleep son parmetros y variables locales y por ello las hebras no comparten datos en esas
sentencias.
El cambio de pthread_mutex_unlock por pthread_mutex_lock hace que el primer consumidor
deje bloqueado el cerrojo, y ya no se desbloquea. Eso crear un bloqueo en todos los procesos.

55

Pregunta 5 (3 puntos) Sept 2012


A continuacin se muestra el programa que se incluye en la practica 8 (Productor/consumidor con
variables condicin y cerrojos). Los nicos cambios respecto al cdigo original son las lneas [42]
y [43], en las que se aaden hebras adicionales con parmetros diferentes. Para facilitar su lectura, se
ha estructurado en dos columnas el cdigo de las funciones Pon y Toma, y Productor y
Consumidor.
[1] #define _REENTRANT
[2]
[3] #include <stdio.h>
[4] #include <stdlib.h>
[5] #include <pthread.h>
[6]
[7] #define Longitud 5
[8] #define Cantidad 20
[9]
[10] int Caja[Longitud];
[11] unsigned Primero = 0; unsigned Ultimo = 0; unsigned
Numero = 0; [12] pthread_mutex_t cerrojo =
PTHREAD_MUTEX_INITIALIZER;
[13] pthread_cond_t huecos =
PTHREAD_COND_INITIALIZER; [14] pthread_cond_t
elementos = PTHREAD_COND_INITIALIZER; [15]
[16] void Pon(int X) {
void Toma(int *pX) {
[17]
pthread_mutex_lock (&cerrojo);
pthread_mutex_lock (&cerrojo);
[18]
while (Numero == Longitud)
while (Numero == 0)
[19]
pthread_cond_wait(&huecos, &cerrojo);
pthread_cond_wait(&elementos,
Caja[Ultimo] = X;
&cerrojo); [20]
*pX = Caja[Primero];
[21]
Ultimo = (Ultimo + 1) % Longitud;
Primero = (Primero + 1) % Longitud;
[22]
Numero++;
Numero--;
[23]
pthread_cond_signal(&elementos);
pthread_cond_signal(&huecos);
[24]
pthread_mutex_unlock(&cerrojo);
pthread_mutex_unlock(&cerrojo);
[25] }
}
[26]
[27] void *Productor (void *pId) {
void *Consumidor(void *pId) {
[28]
unsigned Id = *(unsigned*)pId;
unsigned Id = *(unsigned*)pId;
[29]
unsigned long Periodo = Id * 1000;
unsigned long Periodo = Id * 1000;
[30]
int i;
int Dato; int j;
[31]
for (i = 1; i <= Cantidad; i++) {
for (j = 1; j <= Cantidad;
j++) { [32]
Pon(i + Id);
Toma(&Dato);
[33]
printf("---- Productor %6d produce %6d\n",
printf("**** Consumidor %6d
consume %6d\n", [34]
Id, i+Id);
Id,
Dato);
[35]
usleep(Periodo);
usleep(Periodo);
[36]
}
}
[37]
return NULL;
return NULL;
[38] }
}
[39]
[40] int main(void) {
[41]
pthread_t productor1, productor2; pthread_t
consumidor1,consumidor2; [42] unsigned Id_Productor1=1000;
unsigned Id_Consumidor1=5000;
[43] unsigned Id_Productor2=2000; unsigned
Id_Consumidor2=10000; [44]
[45]
pthread_create(&productor1, NULL, Productor,
&Id_Productor1); [46] pthread_create(&consumidor1, NULL,
Consumidor, &Id_Consumidor1); [47]
pthread_create(&productor2, NULL, Productor, &Id_Productor2);
[48] pthread_create(&consumidor2, NULL, Consumidor,
&Id_Consumidor2); [49]
[50]
pthread_join(productor1, NULL);
[51]
pthread_join(consumidor1, NULL);
[52]
pthread_join(productor2, NULL);
[53]
pthread_join(consumidor2, NULL);
[54] }

A continuacin, se proponen algunos cambios al cdigo y se debe responder a las preguntas


formuladas justi- ficando la respuesta.

Subpregunta 5.1 (0,75 puntos)


Indicar si el comportamiento del programa anterior seguira siendo correcto si se duplica la lnea 23
(tanto en
Pon como en Toma).
como en Toma).

56

Duplicar la lnea 23 no afecta al comportamiento del programa. El signal est dentro del
mutex, y por
tanto ambos signal se ejecutan en exclusin. El primer signal saca de la variable condicin
al menos una de las hebras bloquadas (puede sacar todas). El segundo signal sacar otra (u
otras) hebras. Pero de todas las hebras solo una pasar el while de la lnea 18 (la primera
hebra que consiga reentrar en el mutex).

Subpregunta 5.2 (0,75 puntos)


Indicar si el comportamiento del programa mostrado previamente sigue siendo correcto si se mueve la
lnea 23 a continuacin de la lnea 17 (tanto en Pon como en Toma)?
Mover el signal antes del while puede crear algunos comportamientos raros. Si la hebra no
se queda bloqueada en la variable condicin, el comportamiento sigue siendo el mismo.
Pero si la hebra queda bloqueada, habr desbloqueado, sin haber modificado nada; y
cuando la hebra se desbloquea y sale del while, modifica la regin crtca, pero no
desbloquea hebras que pueden estar esperando.
Supongamos el siguiente
escenario:
La caja est vaca y no hay hebras bloqueadas en variables condicin.
Llega un consumidor, y queda bloqueado (ha ejecutado un signal que no ha tenido efecto.
Varios productores ponen datos hasta llenar la caja (el primero ha desbloqueado al
consumidor, pero suponemos que los productores entran primero). Un ltimo
productor queda bloqueado, por estar llena.
Finalmente reentra el cliente, crea un hueco, pero no desbloquea al productor que est
bloquea- do.
Con el programa dado (dos productores y dos consumidores, que quedan bloqueados en
los usleep) este escenario no puede llegar a darse, pero en un escenario general de
productores consumidores si.

Subpregunta 5.3 (0,75 puntos)


Indicar si el comportamiento del programa mostrado previamente sigue siendo correcto, al
sustituir la sen- tencia while de la lnea 18 de las funciones Pon y Toma por una sentencia if con
las mismas condiciones (if (Numero == Longitud) en Pon y if (Numero == 0) en
Toma).
Si se emplea if en vez de while y el signal desbloquea varias hebras de la variable condicin,
solo una de ellas debera pasar la condicin y con el if entran todas las desbloqueadas. Tambin
puede suceder que antes de entrar la hebra desbloqueda entre en el mutex (desde la lnea 17)
otra hebra y tome el dato o el hueco. La hebra desbloqueada no debera pasar la condicin.

Subpregunta 5.4 (0,75 puntos)


En el programa mostrado, indicar cules son las hebras (productor1, productor2, consumidor1,
consumidor2) que se quedan bloqueadas en un mayor nmero de veces en huecos o elementos.
Las funciones Productor, Consumidor incluyen un usleep que bloquea las hebras una cantidad
de tiempo que depende del parmetro. En esta ejecucin, el tiempo de bloqueo, es mayor en los
con- sumidores, lo que har que los productores llenen la Caja y sean ellos los que queden
bloqueados porque no hay espacio para meter datos en la Caja. En general, los consumidores solo
quedarn blo- queados cuando ejecutan por primera vez su bucle for (si es que antes no se les
han adelantado los
productores).

57

Pregunta 5 Jun 2013


Se ha modificado el cdigo de la prctica 8. Productor/consumidor con variables condicin y
cerrojos. Se ha incluido una tercera hebra (Filtro) que toma lo generado por el productor y se lo enva
al consumidor. Para incluir esta tercera hebra se ha duplicado el array Caja y se han duplicado las
variables Primero, Ultimo y Numero. Primero[i], Ultimo[i] y Numero[i] representan el primero, el ltimo y
el nmero de elementos de la Caja[i]. Antes Primero, Ultimo y Numero eran variables simples, y ahora
son arrays de dos elementos.
Las funciones Pon y Toma acceden a las Cajas con un parmetro adicional numCaja, que indica si
hay que tratar con la Caja 0 o 1. El Productor pone en la caja 0, el Filtro toma de la 0 y pone en la 1, y el
Consumidor toma de la caja 1.

Subpregunta 5.1 (1 punto)


Indicar en qu orden terminarn las tres hebras. Indicar cul de las dos cajas 0 o 1 tiende a estar
llena y cul tiende a estar vaca. Justificar las respuestas
Como en la prctica original, el productor produce mas rpido que el consumidor. y en
esta versin el filtro toma en cuanto tiene datos del productor y entrega los datos al
consumidor siempre que la caja 1 no est llena.
En esta solucin se va llenando la caja 1, y cuando est llena el filtro quedar bloqueado, y
tender a llenarse la caja 0. Entonces quedar bloqueado el productor, y productor y filtro
tendern a ejecutar cuando saca de la caja 1 el consumidor.
Termina primero el productor, luego el filtro (hasta que consiga sacar todo lo pendiente de
sacar del productor), y el ltimo ser el consumidor que ir sancando lo pendiente de sacar
de la caja 1.

Subpregunta 5.2 (2 puntos)


Las dos cajas se gestionan con los mismos mutex y variables condicin. Por ello, no se puede utilizar
de forma simultanea Caja[0] y Caja[1], en las funciones Pon y Toma.
Modificar el cdigo de las funciones Pon y Toma para que se pueda acceder de forma simultnea
a las dos cajas. Se pueden utilizar variables globales adicionales, si se considera necesario.
Creamos mutex y variables condicin para tratar cada caja, y accedemos a las cajas
bloquenado el mutex y variables condicin especficos de la caja.

p t h r e a d _ m u t e x _ t c e r r o j o [ 2 ] = { PTHREAD_MUTEX_INITIALIZER , PTHREAD_MUTEX_INITIALIZER } ;
p t h r e a d _ c o n d _ t h u e c o s [ 2 ] = { PTHREAD_COND_INITIALIZER , PTHREAD_COND_INITIALIZER } ;
p t h r e a d _ c o n d _ t e l e m e n t o s [ 2 ] = { PTHREAD_COND_INITIALIZER , PTHREAD_COND_INITIALIZER } ;
v o i d Pon ( i n t numCaja , i n t X ) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o [ numCaja ] ) ;
w h i l e ( Numero [ numCaja ] == L o n g i t u d )
p t h r e a d _ c o n d _ w a i t (& h u e c o s [ numCaja ] , &c e r r o j o [ numCaja ] ) ;
C a j a [ numCaja ] [ U l t i m o [ numCaja ] ] = X ;
U l t i m o [ numCaja ] = ( U l t i m o [ numCaja ] + 1 ) % L o n g i t u d ;
Numero [ numCaja ]++;
p t h r e a d _ c o n d _ s i g n a l (& e l e m e n t o s [ numCaja ] ) ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o [ numCaja ] ) ;
return ;
}
v o i d Toma ( i n t numCaja , i n t pX ) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o [ numCaja ] ) ;
w h i l e ( Numero [ numCaja ] == 0 )
p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s [ numCaja ] , &c e r r o j o [ numCaja ] ) ;
pX = C a j a [ numCaja ] [ P r i m e r o [ numCaja ] ] ;
P r i m e r o [ numCaja ] = ( P r i m e r o [ numCaja ] + 1 ) % L o n g i t u d ;
Numero [ numCaja];
p t h r e a d _ c o n d _ s i g n a l (& h u e c o s [ numCaja ] ) ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o [ numCaja ] ) ;
return ;

58

71
72
73
74

# include
# include
# include
# include

< s t d i o . h>
< s t d l i b . h>
< p t h r e a d . h>
< t i m e . h>

5
40
41

# define Longitud 5
# d e f i n e C a n t i d a d 20

8
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

i n t Caja [ 2 ] [ Longitud ] ;
unsigned Pr i m e ro [ 2 ] = { 0 , 0 } ;
unsigned Ulti mo [ 2 ] = { 0 , 0 } ;
u n s i g n e d Numero [ 2 ] = { 0 , 0 } ;
p t h r e a d _ m u t e x _ t c e r r o j o = PTHREAD_MUTEX_INITIALIZER ;
p t h r e a d _ c o n d _ t h u e c o s = PTHREAD_COND_INITIALIZER ;
p t h r e a d _ c o n d _ t e l e m e n t o s = PTHREAD_COND_INITIALIZER ;
/ Pone e n e n t e r o en l a c o l a [ numCaja ] , d e t e n i e n d o s e s i no c a b e /
v o i d Pon ( i n t numCaja , i n t X) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o ) ;
w h i l e ( Numero [ numCaja ] == L o n g i t u d )
p t h r e a d _ c o n d _ w a i t (& h u e c o s , &c e r r o j o ) ;
C a j a [ numCaja ] [ U l t i m o [ numCaja ] ] = X ;
U l t i m o [ numCaja ] = ( U l t i m o [ numCaja ] + 1 ) % L o n g i t u d ;
Numero [ numCaja ] + + ;
p t h r e a d _ c o n d _ s i g n a l (& e l e m e n t o s ) ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o ) ;
return ;
}
/ Toma un e n t e r o de l a c o l a [ numCaja ] , d e t e n i e n d o s e s i no h a y /
v o i d Toma ( i n t numCaja , i n t pX ) {
p t h r e a d _ m u t e x _ l o c k (& c e r r o j o ) ;
w h i l e ( Numero [ numCaja ] == 0 )
p t h r e a d _ c o n d _ w a i t (& e l e m e n t o s , &c e r r o j o ) ;
pX = C a j a [ numCaja ] [ P r i m e r o [ numCaja ] ] ;
P r i m e r o [ numCaja ] = ( P r i m e r o [ numCaja ] + 1 ) % L o n g i t u d ;
Numero [ numCaja];
p t h r e a d _ c o n d _ s i g n a l (& h u e c o s ) ;
p t h r e a d _ m u t e x _ u n l o c k (& c e r r o j o ) ;
return ;
}
/ P roc eso que pr odu c e e n t e r o s y l o s e n c o l a /
void P r o d u c t o r ( void pId ) {
unsigned I d = ( unsigned ) p I d ;
unsigned long P e r i o d o = Id 1000;
int i ;
f o r ( i = 1 ; i <= C a n t i d a d ; i ++) {
Pon ( 0 , i + I d ) ;
p r i n t f ( " P r o d u c t o r %6d p r o d u c e %6d \ n " , I d , i + I d ) ;
usleep ( Periodo ) ;
}
r e t u r n NULL ;
}
/ P roc eso que pr odu c e e n t e r o s y l o s e n c o l a /
void F i l t r o ( void pId ) {
unsigned I d = ( unsigned ) p I d ;
i n t Dato , i ;
f o r ( i = 1 ; i <= C a n t i d a d ; i ++) {
Toma ( 0 , & D a t o ) ;
p r i n t f ( " F i l t r o %6d toma y p o n e %6d \ n " , I d , D a t o ) ;
Pon ( 1 , i + I d ) ;
}
r e t u r n NULL ;
}
/ P r o c e s o q u e c o ns u me e n t e r o s /
void Consumidor ( void pI d ) {
unsigned I d = ( unsigned ) p I d ;
unsigned long P e r i o d o = Id 1000;
i n t Dato , j ;
f o r ( j = 1 ; j <= C a n t i d a d ; j ++) {
Toma ( 1 , & D a t o ) ;
p r i n t f ( " C o n s u m i d o r %6d consume %6d \ n " , I d , D a t o ) ;
usleep ( Periodo ) ;
}
r e t u r n NULL ;
}

75
49
50
51
52
53
54
55
56
57
58
59
60

i n t main ( v o i d ) {
p t h r e a d _ t p ro du ct orid , consumidorid , f i l t r o i d ;
unsigned I d _ P r o d u c t o r =1000;
unsigned Id_Consumidor =5000;
unsigned I d _ F i l t r o =2000;
/ cr ear l a s hebras /
p t h r e a d _ c r e a t e (& p r o d u c t o r i d , NULL, P r o d u c t o r , &I d _ P r o d u c t o r ) ;
p t h r e a d _ c r e a t e (& c o n s u m i d o r i d , NULL, C o ns u m i d o r , &I d _ C o n s u m i d o r ) ;
p t h r e a d _ c r e a t e (& f i l t r o i d , NULL, F i l t r o , & I d _ F i l t r o ) ;
/ e s p e r a r a que acaben l a s h eb r a s /
p t h r e a d _ j o i n ( p r o d u c t o r i d , NULL ) ;
p t h r e a d _ j o i n ( c o n s u m i d o r i d , NULL ) ;

59

p t h r e a d _ j o i n ( f i l t r o i d , NULL ) ;
exit (0);

61
62
63

60

Prctica 9. Productor/Consumidor en Java


Especificacin
En primer lugar, se nos pide examinar las siguientes clases:
ColaSincronizada.java
Productor.java
Consumidor.java
PC.java
Todas estas clases, se adjuntan a continuacin:
ColaSincronizada.java
public class ColaSincronizada {
private int[] Caja; /* Se declara la caja o buffer */
private int Primero, Ultimo, Numero; /* Se declaran el
resto de
variables */
public ColaSincronizada(int Longitud) { /*Se inicializa
el buffer */
Caja= new int[Longitud];
Primero = 0;
Ultimo = 0;
Numero = 0;
}
public synchronized void Pon(int X) throws
InterruptedException {
while (Numero == Caja.length) wait(); /* Si la caja est llena,
hay que esperar a que haya algn hueco para seguir produciendo, al
hacer wait, el cerrojo asociado se libera. Cuando se reanuda la
hebra, hay que volver a comprobar al condicin antes de seguir
ejecutando */
Caja[Ultimo] = X;
Ultimo = (Ultimo + 1) % Caja.length;
++Numero; /* Se introduce un nuevo elemento en la caja, y se
incrementa Numero que es donde se lleva la cuenta de cuntos
elementos hay en la caja */
notifyAll(); /* Despierta a todas las hebras que estn esperando
(wait())en la cola de espera, todas pasan a estado listo, a la cola
de entrada al objeto, esperando conseguir el cerrojo, y una de ellas
se ejecutar (conseguir el cerrojo) */
}
public synchronized int Toma() throws
InterruptedException {
while (Numero == 0) wait(); /* Mientras no haya objetos en el
buffer, tenemos que esperar, se pasa a la cola de espera, y se
libera el cerrojo, hasta que recibamos el aviso notifyAll(), en ese
caso, se pasa al estado listo y se intenta conseguir de nuevo el
cerrojo */

61

int X = Caja[Primero];
Primero = (Primero + 1) % Caja.length;
--Numero; /* Se saca un elemento de la caja, y se disminuye el
contador que controla el nmero de elementos que hay en la caja o
buffer */
notifyAll(); /* Se libera el cerrojo, y todas las hebras que
estaban en la cola de espera, pasan a estado listo y se van a la
cola de entrada al objeto, para intentar conseguir el cerrojo */
return X;
}
}
Productor.java
public class Productor extends Thread {
ColaSincronizada cs;
int Id;
public Productor(ColaSincronizada cs, int id) {
this.cs=cs; this.Id=id;
}
public void run() {
for (int i=1; i <= 20; i++)
try {
cs.Pon(i+Id);
System.out.println("---- Productor "+Id+" produce "+
(i+Id));
Thread.sleep(Id);
} catch (Exception e) {}
}
}
Consumidor.java
public class Consumidor extends Thread {
ColaSincronizada cs;
int Id;
public Consumidor(ColaSincronizada cs, int id) {
this.cs=cs; this.Id=id;
}
public void run() {
for (int i=1; i <= 20; i++)
try {
int Dato=cs.Toma();
System.out.println("**** Consumidor "+Id+" consume "+Dato);
Thread.sleep(Id);
} catch (Exception e) {}
}
}

62

PC.java
public class PC

static final int Longitud = 5;


public static void main(String[] args) { ColaSincronizada cs= new
ColaSincronizada(Longitud);
Productor p= new Productor(cs, 1000); Consumidor c=
new Consumidor(cs, 5000);
p.start();
c.start();

/* Importante iniciar con start para que se


ejecutar el run() de la hebra correspondiente
*/

}
}
Fjese que el primer parmetro que se pasa a los constructores de las clases que ejecutan las
hebras ser el monitor que empleen para comunicarse, y el segundo sirve para identificarlas y para
especificar un intervalo de tiempo entre acciones.
Complelos (en el orden en el que vienen), y ejecute PC, explicando su funcionamiento y
resultados. Comprelo con la versin que utiliza monitores POSIX, pcvc/pcvc.c y piense la
importancia que puede tener que en Java no se pueda definir ms de una variable condicin por
monitor.
En primer lugar, se adjunta el resultado de la compilacin de todas las clases (en
el orden correspondiente) y la ejecucin de PC:
javac
javac
javac
javac

ColaSincronizada.java
Productor.java
Consumidor.java
PC.java

java PC
----

Productor 1000 produce 1001

**** Consumidor 5000 consume 1001


---- Productor 1000 produce 1002
---- Productor 1000 produce 1003
---- Productor 1000 produce 1004
---- Productor 1000 produce 1005
**** Consumidor 5000 consume 1002
---- Productor 1000 produce 1006
---- Productor 1000 produce 1007
****
---****
---****
---****

Consumidor 5000 consume 1003


Productor 1000 produce 1008
Consumidor 5000 consume 1004
Productor 1000 produce 1009
Consumidor 5000 consume 1005
Productor 1000 produce 1010
Consumidor 5000 consume 1006

63

---****
---****
---****
---****
---****
---****
---****
---****
---****
---****
****
****
****
****

Productor 1000 produce 1011


Consumidor 5000 consume 1007
Productor 1000 produce 1012
Consumidor 5000 consume 1008
Productor 1000 produce 1013
Consumidor 5000 consume 1009
Productor 1000 produce 1014
Consumidor 5000 consume 1010
Productor 1000 produce 1015
Consumidor 5000 consume 1011
Productor 1000 produce 1016
Consumidor 5000 consume 1012
Productor 1000 produce 1017
Consumidor 5000 consume 1013
Productor 1000 produce 1018
Consumidor 5000 consume 1014
Productor 1000 produce 1019
Consumidor 5000 consume 1015
Productor 1000 produce 1020
Consumidor 5000 consume 1016
Consumidor 5000 consume 1017
Consumidor 5000 consume 1018
Consumidor 5000 consume 1019
Consumidor 5000 consume 1020

Como vemos, la ejecucin es similar a la de los programas pcsem.c y pcvc.c de las


prctias 5 y 6 respectivamente, sin embargo, en esta ocasin, la implementacin se ha realizado con
monitores de java, por lo que es necesario tener en cuenta algunos detalles:

los mtodos sincronizados (Toma y Pon), tienen asociado un cerrojo que hace que se
ejecuten en exclusin mutua.
Si una hebra llama a un mtodo sincronizado y no puede adquirir el cerrojo, se
introduce en una cola asociada al cerrojo.
Al terminar un mtodo sincronizado, se libera el cerrojo, y si hay ms esperando,
se reanuda alguno (algn mtodo sincronizado, es decir, alguna hebra de las que estn en
la cola).
Las variables de condicin tambin son complicadas:

Una hebra es un mtodo sincronizado, se puede suspender haciendo wait().


Si se hace wait() se libera el cerrojo y se incluye en la cola de espera asociada al objeto.
Al hacer notify (): hay que comprobar que la condicin se cumple (ya que se pueden
ejecutar instrucciones antes), la hebra despertada se queda esperando (en la cola de
entrada) a conseguir el cerrojo, el cerrojo se libera cuando se termina el mtodo donde
se hace notify ().

Por lo dems, el programa es similar a los anteriores. Se crean las clases Productor y
Consumidor, y la clase PC (clase principal, donde est el mtodo main) que es la que lanza el programa.
Si nos fijamos en el resultado de la ejecucin, se ve que este sistema es ms eficiente, la
caja tarda ms en llenarse, lo que hace que sea ms rpida la ejecucin.
Como ya se ha comentado en prcticas anteriores, cuando se llena la caja, el productor
se bloquea hasta que el consumidor lee un dato y hace notifyAll(). Esto despierta a todas las
hebras esperando por la condicin y todas pasan a la cola de entrada al objeto, cuando una de
ellas consigue el cerrojo pasa a ejecutarse.
NOTA: si utilizsemos una caja de valor 1, este programa no sera ms eficiente, ni ms rpido que los
de prcticas anteriores, ya que la caja se llena desde el principio y se producen bloqueos.
A continuacin se nos pide lo siguiente:
Modifique la clase PC.java (llamndola Pipeline.java) para que se creen dos colas
sincronizadas iguales, con un productor y un consumidor como los anteriores, pero de modo que

64

el productor escriba en la primera cola, el consumidor lea de la segunda, y la hebra principal lea
20 enteros de la primera cola y los copie, uno a uno, en la segunda, a toda velocidad. Cul es el efecto
neto de este programa?
Se adjuntan a continuacin las modificaciones para conseguir el programa pedido, con la
clase principal Pipeline.java:
Pipeline.java
public class Pipeline

static final int Longitud = 5;


public static void main(String[] args) {
ColaSincronizada cs1= new ColaSincronizada(Longitud);
ColaSincronizada cs2= new ColaSincronizada(Longitud);
Productor p= new Productor(cs1, 1000); Consumidor c=
new Consumidor(cs2, 5000);
p.start();
c.start();
try {
for (int i=1; i<=20; i++){ cs2.Pon(cs1.Toma());
}
}catch(Exception e){}
}
}
El resultado de la ejecucin de este programa es el siguiente:
java Pipeline
---****
-------

Productor 1000 produce 1001


Consumidor 5000 consume 1001
Productor 1000 produce 1002
Productor 1000 produce 1003

----

Productor 1000

produce 1004

----

Productor 1000

produce 1005

**** Consumidor 5000 consume 1002


---- Productor 1000 produce 1006
---- Productor 1000 produce 1007
---- Productor 1000 produce 1008
---- Productor 1000 produce 1009
---- Productor 1000 produce 1010
**** Consumidor 5000 consume 1003
---- Productor 1000 produce 1011
---- Productor 1000 produce 1012
---- Productor 1000 produce 1013
---- Productor 1000 produce 1014
**** Consumidor 5000 consume 1004
---- Productor 1000 produce 1015
**** Consumidor 5000 consume 1005

65

---****
---****
---****
---****
---****
****
****
****
****
****
****
****
****
****
****

Productor 1000 produce 1016


Consumidor 5000 consume 1006
Productor 1000 produce 1017
Consumidor 5000 consume 1007
Productor 1000 produce 1018
Consumidor 5000 consume 1008
Productor 1000 produce 1019
Consumidor 5000 consume 1009
Productor 1000 produce 1020
Consumidor 5000 consume 1010
Consumidor 5000 consume 1011
Consumidor 5000 consume 1012
Consumidor 5000 consume 1013
Consumidor 5000 consume 1014
Consumidor 5000 consume 1015
Consumidor 5000 consume 1016
Consumidor 5000 consume 1017
Consumidor 5000 consume 1018
Consumidor 5000 consume 1019
Consumidor 5000 consume 1020

En este caso, se observa en la ejecucin que este cdigo es todava ms eficiente que el
anterior, lo que es lgico, ya que en este caso, al trabajar el productor sobre una cola, y el
consumidor sobre otra, el productor no se quedar esperando a que el consumidor lea, podr
producir casi continuamente, ya que los datos se copian muy rpido a la otra cola. Lo anterior se
cumplira exactamente si el consumidor y el productor fueran igual de rpidos, pero en este caso,
no es as, hay que tener en cuenta, que en realidad, al ser el consumidor ms lento que el
productor, llegado un momento, el productor se queda esperando, pero cuando
esto se produce, ya se ha ganado en eficiencia.

66

Pregunta 5 (2 puntos) Sept 2006


La praactica 9 (Productor/Consumidor en Java, pcjava) incluye las clases PC, Productor,
Consumidor y ColaSincronizada. Queremos hacer dos cambios:
Queremos que las cabeceras de las clases Productor y Consumidor cambien a:
public class Productor implements Runnable
public class Consumidor implements Runnable
Realizar los cambios necesarios, en las clases Productor, Consumidor y PC para que
el pro- grama funcione de la misma forma que antes.
Queremos cambiar ColaSincronizada por otra estructura que permita almacenar
hasta 2000 elementos, pero que no tenga por quee reservar espacio para todos los elementos
cuando no sea nece- sario, y que podamos saber cuando se nos estaa llenando.
Para ello queremos sustituirla por una instancia de la clase LinkedBlockingQueue del
paque- te java.util.concurrent, de la biblioteca Java. Esta clase solo ocupa
espacios de memoria cuando le es necesario (cuando necesita almacenar los elementos).
Esta clase incluye:
El constructor LinkedBlockingQueue(int capacity)
Que crea una instancia de esa clase con una capacidad maxima fija y una poltica de
acceso por defecto.
El metodo public void put(E o)
Que anade un elemento, esperando si no hay espacio disponible. El parametro puede ser
cual- quier objeto o un valor del cualquier tipo base. Cuando es un valor de tipo base (int,
boolean, char, ...) la maquina java lo transforma en un objeto de la clase
equivalente de la biblioteca (Integer, Boolean, Char, ...).
El metodo public void E take()
Retira un elemento de la cabecera, esperando si no hay elementos disponibles.
Devuelve un objeto que deberemos convertir al tipo que esperemos que nos devuelva.
El metodo public void int remainingCapacity()
Devuelve el numero de elementos que esa cola puede llegar a aceptar sin que se
produzcan bloqueos al anadir.
Actualizar Productor, Consumidor y PC para que trabajen con esta clase, para que los productores produzcan 4000 elementos, los consumidores consuman 4000 elementos, y para que los
productores saquen un mensaje por la pantalla diciendo si la cola esta llena a mas del 80 % antes de an
adir un elemento.
import java.util.concurrent.LinkedBlockingQueue; // Importamos LinkedBlockingQueue
public class Productor implements Runnable {
LinkedBlockingQueue cs; // cambiamos el tipo de cs a LinkedBlockingQueue
int Id;
public Productor(LinkedBlockingQueue cs, int id) { // cambiamos el tipo de cs aLinkedBlockingQueue
this.cs=cs;
this.Id=id;
}
public void run() {
for (int i=1; i <= 4000; i++) // numero de producciones 4000
try {
if (cs.remainingCapacity() < 2000/5) // Miramos si ests casi llena
System.out.println("---- Productor la cola est"s casi llena o llena");
cs.put(i+Id); // cambiamos el nombre del metodo
System.out.println("---- Productor "+Id+" produce "+(i+Id));
Thread.sleep(Id);
} catch (Exception e) {}

67

}
}
import java.util.concurrent.LinkedBlockingQueue; // Importamos LinkedBlockingQueue
public class Consumidor implements Runnable {
LinkedBlockingQueue cs; // cambiamos el tipo de cs a LinkedBlockingQueue
int Id;
public Consumidor(LinkedBlockingQueue cs, int id) { // cambiamos el tipo de cs aLinkedBlockingQueue
this.cs=cs;
this.Id=id;
}
public void run() {
for (int i=1; i <= 4000; i++) // numero de consumiciones 4000
try {
int Dato=(Integer) cs.take(); // Cambiamos el nombre del metodo y convertimos de Object a
Integer System.out.println("**** Consumidor "+Id+" consume "+Dato);
Thread.sleep(Id);
} catch (Exception e) {}
}
}
import java.util.concurrent.LinkedBlockingQueue; // Importamos LinkedBlockingQueue
public class PC

static final int Longitud = 2000; //Tamano de cola 2000


public static void main(String[ ] args) {
LinkedBlockingQueue cs= new LinkedBlockingQueue(Longitud); // Cambiamos ColaSincronizada por LinkedBlockingQueue
Productor p= new Productor(cs, 10);
Consumidor c= new Consumidor(cs, 50);
Thread tp=new Thread(p); // Hay que cambiar la creacio
on de hebra
Thread tc=new Thread(c); // Hay que cambiar la creacio
on de hebra
tp.start(); // Hay que cambiar el arranque
tc.start(); // Hay que cambiar el arranque
}
}

68

Pregunta 5 (3 puntos) Jun 2007


En la praactica 9Productor/Consumidor en Java se quiere modificar la clase
pcjava/ColaSincronizada.java para permitir cambiar dinaamicamente la capacidad de la cola. Se

pide implementar el meetodo cambiaCapacidad, sin modificar las implementaciones de los demaas
meetodos. Es posible introducir en la clase nuevos campos
o meetodos privados si se considera necesario.
El meetodo (cambioCapacidad) debe cumplir los siguientes requisitos funcionales:
Nunca se pierde informacion que tengamos insertada en la cola.
Si se reduce la capacidad y no queda espacio suficiente para almacenar los datos que
ya estan en la cola, el cambio no se debe realizar y se indica el error devolviendo
false. .
Si se incrementa la capacidad, se debera desbloquear a las hebras que estuvieran
esperando por un hueco.
Para almacenar los datos empleamos la clase Java java.util.Vector. De esta clase
emplearemos los metodos siguientes (el tipo de los parametros se ha ajustado a lo que
vamos a utilizar):
Vector(int initialCapacity) // Construye un vector con la
capacidad indicada void set(int index, int element) // Actualiza
un elemento en la posici
on que
// indica el
primer par
ametro int get(int index) // devuelve el
elemento almacenado en la
// posici
on que indica el primer par
ametro
void setSize(int newSize) // actualiza el capacidad del vector

La nueva clase queda:


% Remember to use the lgrind style
\Head{}
\File{ColaSincronizada.java}{2007}{7}{4}{16:44}{1023}
\L{\LB{\K{import}_\V{java}.\V{util}.\V{Vector};_}}
\L{\LB{}}
\L{\LB{\K{public}_\K{class}_\V{ColaSincronizada}_\{_}}
\L{\LB{}}
\L{\LB{}\Tab{4}{\K{private}_\V{Vector}_\V{Caja};_}}
\L{\LB{}\Tab{4}{\K{private}_\K{int}_\V{Primero},_\V{Ultimo},_\V{Numero};_}}
\L{\LB{}\Tab{4}{\K{private}_\K{int}_\V{Capacidad};}}
\L{\LB{}}
\index{ColaSincronizada}\Proc{ColaSincronizada}\L{\LB{}\Tab{4}{\K{public}_\V{Cola
Sincronizada}
\L{\LB{}\Tab{8}{\V{Caja}=_\K{new}_\V{Vector}(\V{Longitud});_}}
\L{\LB{}\Tab{8}{\K{for}_(\K{int}_\V{i}=\N{0};\V{i}_\<_\V{Longitud};_\V{i}++)_\V{C
aja}.\V{add}(
\L{\LB{}\Tab{8}{\V{Primero}_=_\N{0};_}}
\L{\LB{}\Tab{8}{\V{Ultimo}_=_\N{0};_}}
\L{\LB{}\Tab{8}{\V{Numero}_=_\N{0};_}}
\L{\LB{}\Tab{8}{\V{Capacidad}_=_\V{Longitud}}}
\L{\LB{}\Tab{4}{\}_}}
\L{\LB{}}
\index{Pon}\Proc{Pon}\L{\LB{}\Tab{4}{\K{public}_\K{synchronized}_\K{void}_\V{Pon}
(\K{int}_\V{X
\L{\LB{}\Tab{8}{\K{while}_(\V{Numero}_==_\V{Capacidad})_\V{wait}();_}}
\L{\LB{}\Tab{8}{\V{Caja}.\V{set}(\V{Ultimo},\K{new}_\V{Integer}(\V{X}));_}}
\L{\LB{}\Tab{8}{\V{Ultimo}_=_(\V{Ultimo}_+_\N{1})_\%_\V{Capacidad};_}}
\L{\LB{}\Tab{8}{++\V{Numero};_}}
\L{\LB{}\Tab{8}{\V{notifyAll}();_}}

69

\L{\LB{}\Tab{4}{\}_}}
\L{\LB{}}
\index{Toma}\Proc{Toma}\L{\LB{}\Tab{4}{\K{public}_\K{synchronized}_\K{int}_\V{Tom
a}()_\K{throw
\L{\LB{}\Tab{8}{\K{while}_(\V{Numero}_==_\N{0})_\V{wait}();_}}
\L{\LB{}\Tab{8}{\K{int}_\V{X}_=_((\V{Integer})_\V{Caja}.\V{get}(\V{Primero})).\V{
intValue}();_
\L{\LB{}\Tab{8}{\V{Primero}_=_(\V{Primero}_+_\N{1})_\%_\V{Capacidad}();_}}
\L{\LB{}\Tab{8}{\-\-\V{Numero};_}}
\L{\LB{}\Tab{8}{\V{notifyAll}();_}}
\L{\LB{}\Tab{8}{\K{return}_\V{X};_}}
\L{\LB{}\Tab{4}{\}_}}
\L{\LB{}}
\index{cambiaCapacidad}\Proc{cambiaCapacidad}\L{\LB{}\Tab{4}{\K{public}_\K{synchr
onized}_\K{bo
\L{\LB{}\Tab{8}{._._._.}}
\L{\LB{}\Tab{4}{\}_}}
\L{\LB{\}_}}
\L{\LB{}}

El campo Capacidad representa el tamano maximo de la cola


sincronizada.
import java.util.Vector;
public class ColaSincronizada {
private Vector Caja;
private int Primero, Ultimo, Numero;
private int Capacidad;
public ColaSincronizada(int Longitud) {
Caja= new Vector(Longitud);
for (int i=0;i < Longitud; i++) Caja.add(null);
Primero = 0;
Ultimo = 0;
Numero = 0;
Capacidad=Longitud;
}
public synchronized void Pon(int X) throws InterruptedException {
while (Numero == Capacidad) wait();
Caja.set(Ultimo,new Integer(X));
Ultimo = (Ultimo + 1) % Capacidad;
++Numero;
notifyAll();
}
public synchronized int Toma() throws InterruptedException {
while (Numero == 0) wait();
int X = ((Integer) Caja.get(Primero)).intValue();
Primero = (Primero + 1) % Capacidad;
Numero;
notifyAll();
return X;
}
private void reubicaCaja(int nuevaCapacidad) {
//Este metodo supone suponemos que hay espacio suficiente
//para la reubicacion
if (nuevaCapacidad > Capacidad) {
Caja.setSize(nuevoCapacidad);
if (Primero < Ultimo) return;

70

else {
int j=Capacidad;
for (int i=0; i != Ultimo; i++) {
Caja.set(j, Caja.get(i));
j=(j + 1) % nuevaCapacidad;
}
Ultimo=j;
}
}else {
if (Primero < Ultimo) {
if (Ultimo >= nuevaCapacidad) {
int j=nuevaCapacidad;
int i=0;
while (j != Ultimo)
Caja.set(i++, Caja.get(j++));
Ultimo=i;
}
} else {
int j=nuevoCapacidad;
int i=Primero (Capacidad nuevaCapacidad);
Primero=i;
while (j != Capacidad)
Caja.set(i++, Caja.get(j++));
}
Caja.setSize(nuevaCapacidad);
}
Capacidad=nuevaCapacidad;
}
public synchronized boolean cambiaCapacidad(int nuevaCapacidad) {
if (nuevaCapacidad < Numero) return false;
if (Capacidad < nuevaCapacidad) notifyAll();
if (Numero != 0) reubicaCaja(nuevaCapacidad);
return true;
}

71

Pregunta 5 (2,5 puntos) Sept 2008


La praactica 9 Productor/consumidor en Java incluye cuatro clases
(ColaSincronizada, Productor, Consumidor y PC). El meetodo main de la clase PC crea
una instancia de ColaSincronizada que permite comunicar a una instancia de Productor
con otra instancia de Consumidor.
Se quiere crear una clase, llamada Filtro, que filtre los datos que enva el productor antes
de que le lle- guen al consumidor, sin tener que modificar las clases ColaSincronizada,
Productor y Consumidor. El constructor de esta clase es:
Filtro(ColaSincronizada csProductor, ColaSincronizada csConsumidor, int
numeroCiclos)

El filtro recibe de la cola de sincronizacioo n csProductor los datos del productor, y


escribiraa en la cola de sincronizacioon csConsumidor los datos que le llegaraan al consumidor.
En esta praactica, productor y consumidor intercambian nuumeros enteros. El u ltimo paraametro
le indica al filtro cuantos enteros reci- biraa del productor, que es el mismo nuu mero que
deberaa entregar al consumidor. Por cada entero recibido del productor, el filtro ejecutaraa el
meetodo ejecutaFiltro:
protected int ejecutaFiltro(int X) { ... }

que es un meetodo contenido en la clase Filtro y que devolveraa el cuadrado del


paraametro X. El dato que el filtro entrega al consumidor es el valor devuelto por
ejecutaFiltro.
Se pide:
Escribir el coodigo de la clase Filtro.xx
1

public class Filtro extends Thread {

2
3

ColaSincronizada csP;
4
ColaSincronizada csC;
5
int ciclos;
6
Filtro(ColaSincronizada csProductor, ColaSincronizada csConsumidor, int numeroCiclos) { 7
csP=csProductor;
8
csC=csConsumidor;
9
ciclos=numeroCiclos;
10
}
11
12

protected int ejecutaFiltro(int X) {


return 1;
}

13
14
15
16

public void run() {


for (int i=1; i <= ciclos; i++)
try {
int dato=csP.Toma();
dato=ejecutaFiltro(dato);
csC.Pon(dato);
} catch (Exception e) {}
}
}

17
18
19
20
21
22
23
24
25

Escribir la clase Pipeline, modificando la clase PC, para que se creen dos colas
sincronizadas iguales, con un productor y un consumidor y un filtro que pase la informacion
del productor al consumidor lo mas rapido posible.
public class Pipeline {
static final int Longitud = 2;

1
2

72

public static void main(String[ ] args) {

3
4

ColaSincronizada csP= new ColaSincronizada(Longitud);


ColaSincronizada csC= new ColaSincronizada(Longitud);

5
6
7

Productor p= new Productor(csP, 1000);


Filtro f = new Filtro(csP,csC,20);
Consumidor c= new Consumidor(csC, 5000);

8
9
10
11

p.start();
f.start();
c.start();
}
}

12
13
14
15
16

Cual debe ser el tamano de las cola de sincronizacion para que el bloqueo del productor o
del consumidor sea lo mas parecido al del enunciado original de la practica?
El enunciado original tena una u nica cola de sincronizacio n de taman o 5. Cuando el nu
mero de mensajes producidos menos el numero de mensajes consumidos es mayor que 5,
el pro- ductor se bloquea. Cuando el nu mero de mensajes producidos es igual al de
consumidos, el consumidor quedara bloqueado cuando intente sacar un mensaje mas.
En la nueva versio n adema s de las colas de sincronizacio n debemos tener en cuenta la
copia temporal del mensaje que emplea el filtro. El productor quedara bloqueado (en
un tiempo finitio, una vez que el filtro este bloqueado) cuando el nu mero de mensajes
producidos es mayor que la suma de las dos colas mas 1 (la copia temporal del filtro),
menos el nu mero de mensajes consumidos. El consumidor quedara bloqueado (en un
tiempo finito y una vez que el filtro este bloqueado) cuando intente sacar mensaje, y los
mensajes ya consumidos son los mismos que los producidos. El tamano las dos colas debe
ser 2.

73

Pregunta 4 (2,5 puntos) Jun 2009


La prctica Captulo 9. Productor Consumidor en Java implementa una cola sincronizada
(ColaSin- cronizada) con los mtodos Pon y Toma. La solucin que emplea no es muy eficiente, ya que
los notifyAll desbloquean a todas las hebras bloquedas, y adems el mtodo Toma desbloquea tambin a
hebras consu- midor, y sera suficiente desbloquear slo a las productor (ya que en Toma hemos creado
el hueco que los productores estaban esperando, y a los consumidores los debera desbloquear el Pon).
A Pon le sucede lo mismo respecto de las hebras consumidor.
Pensamos en utilizar una sincronizacin explcita (frente a la implcita de los modificadores de mtodo
synchronized que utiliza la solucin de la prctica). Java incluye la sentencia:
syncronized ( obj ) {
/ / b l o q u e de s e n t e n c i a s J a v a c o n l l a m a d a s a o b j . w a i t ( ) y o b j . n o t i f y A l l ( )
}

Esta sentencia bloquea el objeto obj; otra hebra que intente ejecutar una sentencia syncronized
sobre ese objeto quedar bloqueada hasta que la hebra que entr primero, termine el bloque de la
sentencia que ejecuta sobre ese mismo objeto.
Si en el bloque de una sentencia synchronized que bloquea el objeto obj se ejecuta la llamada obj.wait(),
la hebra queda bloqueada, desbloqueando la entrada al objeto. Las llamadas obj.notify() y
obj.notifyAll() desbloquean las hebras bloqueadas con wait sobre el objeto, y las hebras desbloqueadas
volvern a pedir la exclusin del objeto, y seguir con la sentencia que segua al wait.
Modificamos los mtodos Pon y Toma del enunciado de la prctica de la siguiente forma:
public c l a ss ColaSincronizadaEx {
p r i v a t e i n t [ ] Caja ;
p r i v a t e i n t P r i m e r o , U l t i m o , Numero ;
p r i v a t e O b j e c t h u e c o s = new O b j e c t ( ) ;
p r i v a t e O b j e c t e l e m e n t o s = new O b j e c t ( ) ;
p r i v a t e boolean l l e n o = f a l s e ;
p r i v a t e boolean va c i o = true ;
p u b l i c v o i d Pon ( i n t X) t h r o w s I n t e r r u p t e d E x c e p t i o n {
while ( l l e n o )
synchronized ( huecos )
{ huecos . wait ( ) ;
}
Caja [ Ultimo ] = X;
U l t i m o = ( U l t i m o + 1) % C a j a . l e n g t h ;
++Numero ;
vacio = f a l s e ;
l l e n o = Numero == C a j a . l e n g t h ;
synchronized ( elementos )
{ elementos . n o t if y ( ) ;
}
}
p u b l i c i n t Toma ( ) t h r o w s I n t e r r u p t e d E x c e p t i o n {
while ( vacio )
synchronized ( elementos )
{ elementos . wait ( ) ;
}
i n t X = Caja [ Primero ] ;
P r i m e r o = ( P r i m e r o + 1) % C a j a . l e n g t
h ; Numero;
lleno = false ;
v a c i o = Numero == 0 ;

74

synchronized ( huecos )
{ huecos . n o t i f y ( ) ;
}
r et u r n X;
}
}

Se pide: en caso de que la solucin sea correcta justificar por qu. Si la solucin es incorrecta
proponer una solucin vlida basada en sentencias de sincronizacin explcita.
La solucin propuesta es incorrecta. Solo podra funcionar con un solo productor, un solo
consumidor, y una cola de un nico elemento.
En cualquier otro caso, varios productores y varios consumidores pueden acceder de forma
simultanea a las variables Caja, Primero, Ultimo, Numero, que deben ser actualizadas en exclusin, ya que si varias hebras acceden a ellas de forma simultanea podran
quedar inconsistentes. Por ejemplo, si quedando un solo hueco, varios productores acceden de
forma simultanea, todos ellos prodran pasar el while del mtodo Pon, y acceder todos ellos a
Caja, en donde queda un nico hueco.
Es necesario un monitor que garantice la exclusin en la caja y sus variables. La solucin
siguiente emplea una variable adicional para hacer exclusin (memoriaCompartida). Se
dejan huecos y elementos como colas de espera de productores y consumidores. Es necesario volver a comprobar si la caja est llena o vaca, porque varios productores pueden
pasar la condicin inicial de los huecos, quedando un nico hueco (e igual con los elemen- tos
para los consumidores). Slo en el caso de que varios productores o varios consumidores lleguen
de forma simulanea para ocupar un ltimo hueco, o para sacar un nico elementos, todos menos
uno quedan bloqueados en el wait de memoriaCompartida.
p ub lic c l a s s ColaSincroniza daExSol {
p r i v a t e i n t [ ] Caja ;
p r i v a t e i n t P r i m e r o , U l t i m o , Numero ;
p r i v a t e O b j e c t h u e c o s = new O b j e c t ( ) ;
p r i v a t e O b j e c t e l e m e n t o s = new O b j e c t ( ) ;
p r i v a t e O b j e c t m e m o r i a C o m p a r t i d a = new O b j e c t ( ) ;
p r i v a t e boolean l l e n o = f a l s e ;
p r i v a t e boolean v a c io = t r u e ;
p u b l i c v o i d Pon ( i n t X ) t h r o w s I n t e r r u p t e d E x c e p t i o n {
synchro nized ( huecos ) {
while ( l l e n o )
huecos . wa it ( ) ;
}
s y n c h r o n i z e d ( me mo riaCo mp a rti da ) {
w h i l e ( l l e n o ) me moria Co mp a rti da . w a i t ( ) ;
Ca ja [ U l t i m o ] = X ;
U l t i m o = ( U l t i m o + 1) % C a j a . l e n g t h ;
++Numero ;
vacio = fa l s e ;
l l e n o = Numero == C a j a . l e n g t h ;
mem oria Co mpa rtid a . n o t i f y A l l ( ) ;
}
synchronized ( e l e m e n t o s ) { e le me n t o s . n o t i f y

();

}
}
p u b l i c i n t Toma ( ) t h r o w s I n t e r r u p t e d E x c e p t i o n {
i n t X;
synchronized ( e l e m e n t o s ) {
while ( v a c io )
e le me n to s . wait ( ) ;
}

75

s y n c h r o n i z e d ( me mo riaCo mp a rti da ) {
w h i l e ( l l e n o ) me moria Co mp a rti da . w a i t ( ) ;
X = Caja [ Primero ] ;
P r i m e r o = ( P r i m e r o + 1) % C a j a . l e n g t h ;
Numero;
lleno = false ;
v a c i o = Numero == 0 ;
mem oria Co mpa rtid a . n o t i f y A l l ( ) ;
}
synchro nized ( huecos ) {
huecos . n o t i f y ( ) ;
}
r e t u r n X;
}
}

76

Pregunta 3 (2 puntos) Sept 2010


Se quiere cambiar la prctica 9. Productor/consumidor en Java, y plantear una solucin que
reutilice la clase de la biblioteca Vector cuya implementacin utiliza mtodos sincronizados y se
comporta como un monitor. En la realidad es una clase genrica pero para evitar confusiones, en este
ejercicio se tratar como un vector de int.
Los mtodos de la clase relevantes para este ejercicio, son los siguientes:
b p u b l i c b o o l e a n a dd ( i n t e ) / / Aade un e l e m e n t o e , como l t i m o e l e m e n t o d e l v e c t o r ;

/ / Se s u p o n d r que s i e m p r e d e v u e l v e t r u e
3
4

public int s i ze ( )

/ / D e v u e l v e e l nme r o de e l e m e n t o s a a d i d o s y no r e t i r a d o s

p u b l i c boolean is E mpty ( )

/ / D e v u e l v e t r u e c uando s i z e ( ) == 0

5
6
7
75
76
77

p u b l i c i n t f i r s t E l e m e n t ( ) thr ows N o S u c h E l e m e n t E x c e p t i o n
/ / D e v u e l v e e l e l e m e n t o en l a p o s i c i n 0 .
/ / E l e l e m e n t o no s e e l i m i n a d e l v e c t o r

11
42
43
44
45
46

p u b l i c v o i d r e m o v e E l e m e n t A t ( i n t i n d e x ) thr ows N o S u c h E l e m e n t E x c e p t i o n
/ / E l i m i n a e l e l e m e n t o e n l a p o s i c i n i n d e x ; e l que
/ / s e e n c o n t r a s e e n l a p o s i c i n i n d e x +1 p a s a a o c u p a r
/ / la posicin index ;
/ / l a l o n g i t u d d e l v e c t o r s e d e c r e me n ta en 1

La primera solucin planteada es la siguiente:


1

impor t j a v a . u t i l . V e c t o r ;

public cla ss ColaSincronizada {


p r i v a t e Vector < I n t e g e r > Ca ja ;
98
pr iv a te i n t Longitud ;
99
public Co l a Si n c ro n i z a da ( i nt Longitud ) {
100
C a j a = new V e c t o r < I n t e g e r > ( ) ;
101
t h i s . Longitud =Longitud ;
102
}
103
p u b l i c v o i d Pon ( i n t X) thr ows I n t e r r u p t e d E x c e p t i o n {
104
synchronized ( t h i s ) {
105
w h i l e ( C a j a . s i z e ( ) == L o n g i t u d ) w a i t ( ) ;
106
}
107
C a j a . a dd (X ) ;
108
notifyAll ( );
109
}
110
p u b l i c i n t Toma ( ) thr ows I n t e r r u p t e d E x c e p t i o n {
111
synchronized ( t h i s ) {
112
w hi le ( C a ja . isEmpty ( ) ) w a i t ( ) ;
113
}
114
i nt X = Caja . f i r s t E l e m e n t ( ) ;
115
C a j a . re moveE lementAt ( 0 ) ;
116
notifyAll ( );
117
re turn X;
118
}
119 }
96
97

La sentencia synchronized (this) { ...} ejecuta un bloque de cdigo (el cuerpo de la sentencia),
con exclusin mutua respecto a otros bloques o mtodos tambin declarados synchronized en el objeto
que referencia this.
Se pide:
Al depurar el programa se han detectado casos aleatorios en los que Caja tiene mas elementos
que el valor de Longitud y hay veces que al llamar a firstElement o removeElementAt saltan
excepciones. Explicar por qu esta solucin no funciona.
Plantear una solucin que resuelva estos problemas.
El problema es que las operaciones size-add y isEmpty-firstElement-removeElementAt deben
ejecutarse con exclusin mutua, y sus ejecuciones no se pueden intercalar. Si lo hacemos como
en la solucin planteada, aunque esas operaciones se ejecuten con synchronized, desde que
consultamos size hasta que ejecutamos add, el valor de size puede haber cambiado. Y lo mismo
con isEmpty-firstElement- removeElementAt, podemos extraer el primero, y que cuando borramos, el
que extrajimos ya no sea el primero.

77

Una solucin es:


1

im port ja v a . u t i l . V e c t o r ;

2
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

public c l as s ColaSincronizada {
p r i v a t e V e c t o r < I n t e g e r > C aja ;
pr ivat e in t Longitud ;
publ ic ColaSincronizada ( i n t Longitud ) {
C aja= new V e c t o r < I n t e g e r > ( ) ;
t h i s . L o n g i t u d= L o n g i t u d ;
}
p u b l i c v o i d Pon ( i n t X ) t h r o w s I n t e r r u p t e d E x c e p t i o n {
synchronized ( t h i s ) {
w h i l e ( C aja . s i z e ( ) == L o n g i t u d ) w a i t ( ) ;
C aja . add ( X ) ;
}
notifyAll ();
}
p u b l i c i n t Toma ( ) t h r o w s I n t e r r u p t e d E x c e p t i o n {
synchronized ( t h i s ) {
w h i l e ( C aja . i s E m p t y ( ) ) w a i t ( ) ;
i n t X = C aja . f i r s t E l e m e n t ( ) ;
C aja . r e m o v e E l e m e n t A t ( 0 ) ;
}
notifyAll ();
r e tur n X;
}
}

78

Prctica 11. Comunicaciones con RMI (Versin antigua)


Especificacin
En primer lugar, tenemos que examinar la interfaz ServidorFicheros.java y las clases
ServidorFicherosImpl.java y ClienteDeFicheros.java, que se adjuntan a continuacin:
ServidorFicheros.java
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.io.File;
public interface ServidorFicheros extends Remote {
public File open(String nombreFichero)
throws RemoteException;
public byte[] read(File in, int numBytes)
throws RemoteException;
}
ServidorFicherosImpl.java
import
import
import
import
import
import

java.io.File;
java.io.FileInputStream;
java.rmi.*;
java.rmi.server.UnicastRemoteObject;
java.util.Map;
java.util.HashMap;

public class ServidorFicherosImpl extends


UnicastRemoteObject
implements ServidorFicheros {
private java.util.Map<File,FileInputStream> ficheros;
protected String nombre;
public ServidorFicherosImpl(String s) throws
RemoteException{
super();
nombre = s;
ficheros = new HashMap<File,FileInputStream>();
}
public synchronized File open(String nombreFichero) {
try {
File key=new File(nombreFichero); FileInputStream
input=new FileInputStream(key);
ficheros.put(key,input);
return key;
} catch (Exception e) { System.err.println("Error
ejecutando open de :"+nombreFichero);
e.printStackTrace();
return null;
}
}
public synchronized byte[] read(File in, int numBytes) {
if (in == null) {
System.err.println("Error por llamada read sobre un fichero
no inicializado");

79

return null;
}
try {
FileInputStream input=ficheros.get(in);
int restan=input.available();
byte[] buf;
if (restan < numBytes)
buf=new byte[restan];
else
buf=new byte[numBytes];
if (input == null) {
System.err.println("Error ejecutando read sobre :
"+in.getName());
return null;
}
input.read(buf);
return buf;
} catch (Exception e) {
System.err.println("Error ejecutando read sobre :
"+in.getName());
e.printStackTrace();
return null;
}
}
public static void main(String argv[]) {
if(System.getSecurityManager() == null) {
System.setSecurityManager(new
RMISecurityManager());
}
try {

ServidorFicheros fi = new
ServidorFicherosImpl("ServidorFicheros");
Naming.rebind("//127.0.0.1/ServidorFicheros", fi);
} catch(Exception e) {
System.out.println("ServidorFicheros:
"+e.getMessage());
e.printStackTrace();
}
}
}
ClienteDeFicheros.java
import java.io.*;
import java.rmi.*;
public class ClienteDeFicheros {
public static void main(String argv[]) {
if(argv.length != 2) { System.out.println("Usar
ClienteDeFicheros:nombreFichero nombreMaquina");
System.exit(0);
}
try {
String nombre = "//" + argv[1] +
"/ServidorFicheros";

80

ServidorFicheros fs = (ServidorFicheros)
Naming.lookup(nombre);
File fi=fs.open(argv[0]);
byte[] datos;
do {
datos=fs.read(fi,10);
System.out.print(new String(datos));
} while (datos.length == 10);
} catch(Exception e) { System.err.println("Excepcion en
Servidor de
Ficheros: "+ e.getMessage());
e.printStackTrace();
}
}
}
A continuacin, se adjunta la explicacin de lo que hay que hacer con el interfaz y estas
clases:
Complelos (es necesario compilar primero el interfaz y luego las clases). Hay dos mtodos
main (uno en ServidorFicherosImpl y otro en ClienteDeFicheros).

Para poder ejecutar es necesario crear los stubs. Eso lo haremos, despus de compilar, con:
% rmic ServidorFicherosImpl
rmic es un programa que se encuentra en la instalacin de Java. Este programa generar
ServidorFicherosImpl_Stub.class. Para hacerlo funcionar debemos ejecutar (en la misma
mquina donde ejecutaremos ServidorFicherosImpl):
% rmiregistry
&
Para evitar problemas de seguridad con el gestor de seguridad de RMI, debemos ejecutar el servidor
RMI
con
las
siguientes
opciones
de
seguridad
(contenidas
en
el fichero
rmi/permisosServidor.txt):
grant
{
permission java.security.AllPermission "", "";
}
;
La ejecucin del servidor y el cliente se hace en mquinas virtuales java diferentes, y si
es posible, en mquinas fsicas diferentes conectadas:
%java -Djava.security.policy=permisosServidor.txt ServidorFicherosImpl
En otro interprete de rdenes:
%java ClienteDeFicheros Fichero Maquina
Donde Fichero es el nombre de un fichero del sistema de ficheros de la mquina donde ejecuta el
servidor (ServidorFicherosImpl), y Maquina es el nombre o la direccin de la mquina
donde se ejecuta rmiregistry (en esta prctica la misma que donde ejecuta el servidor).

81

Para ejecutar el cliente, en el mismo directorio (o accesible con classpath) debe estar
el stub (ServidorFicherosImpl_Stub.class).
Ejecutar el mismo programa con dos clientes, y el servidor, iniciando en un intrprete
de rdenes diferente cada uno de los tres, y si es posible en mquinas diferentes. Los clientes
accedern a ficheros remotos diferentes.
A continuacin se adjunta el proceso seguido para realizar las operaciones antes
descritas:
javac ServidorFicheros.java javac
ServidorFicherosImpl.java javac
ClienteDeFicheros.java
rmic ServidorFicherosImpl
rmregistry &
java Djava.security.policy=permisosServidor.txt ServidorFicherosImpl
(lo que sigue se ejecuta en otro intrprete de rdenes, normalmente Maquina=localhost)
java ClienteDeFicheros fichero.txt Maquina
Comentarios a la prctica:
Esta prctica permite acceder a ficheros situados en otra mquina a la que tenemos
conectada la nuestra (o en plataformas diferentes). Para ello, se implementa la interfaz del servidor de
ficheros y las clases ServidorFicherosImpl y ClienteDeFicheros.
Para compilar hay que hacerlo en orden: se compila primero la interfaz, y luego las clases.
Despus de esto, debemos crear la tubera de comunicacin entre ambas mquinas (creacin de
stubs):
Creacin de tubera: rmic ServidorFicherosImpl.
Para hacerlo funcionar escribimos: rmiregistry &.
La siguiente lnea evita problemas de seguridad:
java -Djava.security.policy=permisosServidor.txt ServidorFicherosImpl.
En otro intrprete de rdenes se ejecuta:
java ClienteDeFicheros Fichero.txt Maquina (en mquina se pone localhost, o el nombre de
la mquina).
En esta ventana (en el segundo intrprete de rdenes) se muestra el contenido del fichero al que
se quiere acceder.
Nota:
En hasmap hay dos entradas por archivo, la primera de ellas es el nombre del fichero (con
un puntero al archivo), y lasegunda es el input Stream asociado a dicho fichero, que nos
permitir trabajar con l (abrirlo, cerrarlo...). Esto es lo que permite la comunicacin para el
acceso al fichero.
El programa funciona igual para dos clientes.
Se nos pide, por ltimo, una versin modificada del programa que incluya un nuevo
mtodo close en el interfaz ServidorFicheros que cierre un fichero que open ha devuelto
anteriormente, y que no se volver a utilizar.
La modificacin pedida consiste en lo siguiente:

En la interfaz, hay que definir el nmero mtodo clase.


En la clase ClienteDeFicheros hay que aadir una lnea que cierra el fichero identificado
por fs.

82

En la clase ServidorFicherosImpl hay que escribir la implementacin del mtodo clase, que
es la siguiente: se crea un objeto de tipo fichero a travs de su nombre, luego obtenemos
su flujo de datos con get y lo utilizamos para cerrarlo.
Ejecutamos del mismo modo que la vez anterior y observamos que el fichero se ha cerrado.

83

84

Prctica 11. Comunicaciones con RMI (Versin moderna)


Especificacin
Examine las siguientes interfaces
ServidorFicheros.java
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ServidorFicheros extends Remote {


public byte[] read(String nombreFichero, int numBytes, long pos)
throws RemoteException;
public void write(String nombreFichero, byte[] datos, long pos)
throws RemoteException;
public boolean delete(String nombre_fichero)
throws RemoteException;
}

ServidorFicherosImpl.java
import
import
import
import

java.io.File;
java.io.RandomAccessFile;
java.rmi.*;
java.rmi.server.UnicastRemoteObject;

public class ServidorFicherosImpl


extends UnicastRemoteObject
implements ServidorFicheros {
public ServidorFicherosImpl() throws RemoteException {
super();
}
public synchronized byte[] read(String nombreFichero, int numBytes, long
pos) {
try {
RandomAccessFile f= new RandomAccessFile(nombreFichero, "r");
f.seek(pos);
int cuenta;
byte[] buf=new byte[numBytes];
cuenta= f.read(buf);
f.close();
if (cuenta < numBytes) {
byte[]shortbuf=new byte[cuenta];
System.arraycopy(buf, 0, shortbuf, 0, cuenta);
return shortbuf;
}
else
return buf;
}
catch (Exception e) {
System.err.println("Error "
+ e.getMessage()
+ " ejecutando read sobre: "
+ nombreFichero);
return null;
}
}
public synchronized void write(String nombreFichero, byte[] datos, long
pos) {
try {
RandomAccessFile f= new RandomAccessFile(nombreFichero, "rw");

85

f.seek(pos);
f.write(datos);
f.close();
return;
}
catch (Exception e) {
System.err.println("Error "
+ e.getMessage()
+ " ejecutando write sobre: "
+ nombreFichero);
return;
}
}
public synchronized boolean delete(String nombre_fichero) {
try {
File f = new File(nombre_fichero);
f.delete();
return true;
}
catch (Exception e) {
System.err.println("Error "
+ e.getMessage()
+ " ejecutando delete sobre: "
+ nombre_fichero);
//falta implementar la excepci
return false;
}
}
public static void main(String argv[]) {
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
try {
ServidorFicheros fi = new ServidorFicherosImpl();
Naming.rebind("//127.0.0.1/ServidorFicheros", fi);
}
catch(Exception e) {
System.out.println("ServidorFicheros: "+e.getMessage());
}
}
}

RMIlee.java
import java.io.*;
import java.rmi.*;
public class RMIlee {
public static void main(String argv[]) {
if (argv.length != 2) {System.err.println("Uso: RMIlee nombreFichero
nombreMaquina"); System.exit(1);}
try {
String nombre = "//" + argv[1] + "/ServidorFicheros";
ServidorFicheros fs = (ServidorFicheros) Naming.lookup(nombre);
byte[] datos;
long pos = 0;
do {
datos=fs.read(argv[0],1024, pos);
System.out.write(datos);
pos+= 1024;
}
while (datos.length == 1024);
}
catch (RemoteException e) { System.err.println("Excepcion remota: "+
e.getMessage()); }
catch (Exception e) { System.err.println("Excepcion: "+
e.getMessage()); }
}
}

86

RMIesc.java
import java.io.File;
import java.io.FileInputStream;
import java.io.*;
import java.rmi.*;
public class RMIesc {
public static void main(String argv[]) {
if (argv.length != 2) {System.err.println("Uso: RMIesc nombreFichero
nombreMaquina"); System.exit(1);}
try {
String nombre = "//" + argv[1] + "/ServidorFicheros";
ServidorFicheros fs = (ServidorFicheros) Naming.lookup(nombre);
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
String line = null;
line = br.readLine();
long pos = 0;
while (line != null) {
line+= "\n";
byte[] data = line.getBytes();
fs.write(argv[0], data, pos);
pos += data.length;
line = br.readLine();
}
}
catch (RemoteException e) { System.err.println("Excepcion remota: "+
e.getMessage()); }
catch (Exception e) { System.err.println("Excepcion: "+
e.getMessage()); }
}
}

Se pide realizar otro cliente denominado RMImv que tome cuatro parmetros: el nombre del fichero a
mover, la mquina origen, el nombre que tendr e destino y la mquina destino. Para ello hay que
implementar un nuevo mtodo remoto: delete, que en el servidor puede usar el mtodo delete de un
java.io.File local.
RMImv
import
import
import
import

java.io.File;
java.io.FileInputStream;
java.io.*;
java.rmi.*;

public class RMImv {


public static void main(String argv[]) {
if (argv.length != 4) {System.err.println("Uso: RMIlee nombreFichero
nombreMaquina"); System.exit(1);}
try {
String nombre1 = "//" + argv[1] + "/ServidorFicheros2";
String nombre2 = "//" + argv[3] + "/ServidorFicheros2";
ServidorFicheros2 fs = (ServidorFicheros2) Naming.lookup(nombre1);
ServidorFicheros2 fs2= (ServidorFicheros2) Naming.lookup(nombre2);
byte[] datos;
long pos = 0;
do {
datos=fs.read(argv[0],1024, pos);
fs2.write(argv[2], datos, pos);
pos+= 1024;
}
while (datos.length == 1024);

87

}
catch (RemoteException e) { System.err.println("Excepcion remota: "+
e.getMessage()); }
catch (Exception e) { System.err.println("Excepcion: "+ e.getMessage());
}
}
}

ServidorFicheros2
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface ServidorFicheros2 extends Remote {
public byte[] read(String nombreFichero, int numBytes, long pos)
throws RemoteException;
public void write(String nombreFichero, byte[] datos, long pos)
throws RemoteException;
public void delete(String nombreFichero)
throws RemoteException;

88

Pregunta 6 (2 puntos) Jun 2006


Entregue o escriba la modificacioon que hizo a la praactica de Comunicaciones con
RMI para cerrar ficheros remotos, explicaandola en detalle.
Modifique ademaas la interfaz ServidorFicheros y la clase
ServidorFicherosImpl para que incluyan un meetodo que permita borrar un
fichero, dado su nombre. Ese meetodo de- volveraa un boolean para confirmar que el
fichero ha sido borrado. Puede emplear el meetodo boolean delete(), de la clase
File para borrarlo.
ServidorFicheros queda:
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.io.File;
public interface ServidorFicheros extends Remote {
public File open(String nombreFichero) throws RemoteException;
public byte[ ] read(File in, int numBytes) throws RemoteException;
public void Close(File ficheroACerrar) throws RemoteException;
public boolean delete(String nombreFichero) throws
RemoteException;

ServidorFicherosImpl queda:
public synchronized void close(File ficheroACerrar)
{
FileInputStream fich=ficheros.get(ficheroACerrar);
if (fich == null) {
System.err.println("Error ejecutando close sobre : "+ficheroACerrar.getName());
return;
}
fich.close();
ficheros.remove(ficheroACerrar);
return;
}
public synchronized boolean delete(String nombreFichero) {
for (File keys : ficheros.keySet())
if (keys.getName().equals(nombreFichero)) return false;
File key=new File(nombreFichero);
return key.delete();
}

89

Pregunta 5 (2 puntos) Sept 2007


La practica 11 Comunicaciones con RMI implementa los metodos open y read con modificadores
synchronized para garantizar la consistencia de la estructura de datos ficheros. Pero el metodo read
so lo accede a esa estructura en las primeras lneas, y mantiene bloqueado el monitor toda la
ejecucio n del metodo (incluida la ejecucio n del metodo read de FileInputStream que es la que
debera tener mayor tiempo de ejecucion).
Se pide, cambiar la implementacion de read y open y el constructor de ServidorFicherosImpl para quitar
los modificadores synchronized, pero seguir garantizando la consistencia de la estructura ficheros,
mediante
la
clase
java.util.concurrent.locks.ReentrantLock,
que
implementa
el
interface
java.util.concurrent.locks.Lock:
public interface Lock {
void lock(); // bloquea RC
void unlock(); // desbloquea la RC
}
import java.io.File;
import java.io.FileInputStream;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;
import java.util.HashMap;
// cambio
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ServidorFicherosImpl extends UnicastRemoteObject
implements ServidorFicheros {
private java.util.Map<File,FileInputStream> ficheros;
protected String nombre;
// cambio
private Lock monitor;

public ServidorFicherosImpl(String s) throws RemoteException{


super();
nombre = s;
ficheros = new HashMap<File,FileInputStream>();
// cambio
monitor=new ReentrantLock();
}
// cambio

try {
File key=new File(nombreFichero);
FileInputStream input=new FileInputStream(key);

ficheros.put(key,input);
System.err.println("Error ejecutando open de : "+nombreFichero);
e.printStackTrace();
monitor.unlock();

90

return key;
} catch (Exception e) {
System.err.println("Error ejecutando open de : "+nombreFichero);
e.printStackTrace();
return null;
}
}
// cambio

if (in == null) {
System.err.println("Error por llamada read sobre un fichero no inicializado");
return null;
}
try {

FileInputStream input=ficheros.get(in);
monitor.unlock();

if (input == null) {
System.err.println("Error ejecutando read sobre : "+in.getName());
return null;
}
int restan=input.available();
byte[ ] buf ;
if (restan < numBytes)
buf =new byte[restan];
else
buf =new byte[numBytes];
input.read(buf );
return buf ;
} catch (Exception e) {
System.err.println("Error ejecutando read sobre : "+in.getName());
e.printStackTrace();
return null;
}
}
}

91

Pregunta 5 (2.5 puntos) Jun 2008


La praactica 11 (Comunicaciones con RMI) implementa los meetodos remotos open y read. Se quiere
annadir un nuevo meetodo print para imprimir un fichero en el servidor remoto. Este meetodo tiene como
argumento el fichero a imprimir, que es de tipo File, obtenido previamente con open.
Se quiere evitar que los clientes que llamen a print tengan que esperar hasta el final de la impresioon.
Para ello se debe utilizar una hebra que seraa la que realice las tareas de impresioon. De esta forma, el meetodo
print podraa acabar antes del fin de la impresioon.
Se pide, incluir el meetodo print en el interfaz ServidorFicheros e implementarlo en la clase ServidorFicherosImpl, junto con cualquier clase o meetodo que sea necesario. No se requiere tener en cuenta los
aspectos relativos a las excepciones.
La implementacioon de print se debe hacer utilizando el meetodo:
protected void printFile(FileInputStream fi)
que se supone implementado en la clase ServidorFicherosImpl.
public interface ServidorFicheros extends Remote{
public synchronized File open(String nombreFichero) throws
RemoteException;
public synchronized byte[] read(File in, int numBytes) throws
RemoteException;
public synchronized void print(File in) throws RemoteException;
}
public class ServidorFicherosImpl extends UnicastRemoteObject implements ServidorFicheros
{
private java.util.Map<File,FileInputStream> ficheros;
public synchronized File open(String nombreFichero) {...}
public synchronized byte[] read(File in, int numBytes)
{...}
public synchronized void print(File in) throws RemoteException {
if (in == null) {
System.err.println("Error por llamada read sobre un fichero no inicializado");
return;
}
FileInputStream input=ficheros.get(in);
new Impresora(input,this).start();
}
protected void printFile(FileInputStream fi) { ... }
private class Impresora extends Thread {
FileInputStream fichero;
ServidorFicherosImpl serv;
public Impresora(FileInputStream fi, ServidorFicherosImpl sf) {
fichero=fi;
serv=sf;
}
public void run() {
serv.printFile(fichero);
}
}
}

92

Pregunta 5 Jun 2010


Se dispone de un sencillsimo servidor de ficheros sin estado que utiliza los nombres de los
ficheros como referencia a los mismos. Aunque es ineficiente, es muy sencillo y fiable1 . No hay
necesidad de abrir los ficheros y las lecturas y escrituras indican en qu posicin del fichero estn los
datos. La visin del cliente es:
impor t j a v a . r m i . Remote ;
impor t j a v a . r m i . R e m o t e E x c e p t i o n ;
impor t j a v a . i o . F i l e ;
4
78
79
80
81

p u b l i c i n t e r f a c e S e r v i d o r F i c h e r o s e x t e n d s Remote {
p u b l i c b y t e [ ] r e a d ( S t r i n g n o m b r e F i c h e r o , i n t numBytes , l o n g p o s ) thr ows R e m o t e E x c e p t i o n ;
p u b l i c v o i d w r i t e ( S t r i n g n o m b r e F i c h e r o , b y t e [ ] d a t o s , l o n g p o s ) thr ows R e m o t e E x c e p t i o n ;
}

Y su realizacin en el servidor es la que sigue. Observe que se usan ficheros de acceso


aleatorio. Por cada operacin, se abre el fichero, se ajusta la posicin a la que indica el parmetro,
se lee o escribe secuencialmente desde ese lugar y seguidamente se cierra el fichero.
47
48
49
50

impor t
impor t
impor t
impor t

java . io . File ;
j a v a . i o . R a n d o mA c c e s s Fi l e ;
j a v a . r mi . ;
j a v a . r mi . s e r v e r . U n i c a s t R e m o t e O b j e c t ;

5
6

p u b l i c c l a s s S e r v i d o r F i c h e r o s I m p l e x t e n d s U n i c a s t R e mo t e O b j e c t i mpl e ment s S e r v i d o r F i c h e r o s {

p u b l i c S e r v i d o r F i c h e r o s I m p l ( ) thr ows R e m o t e E x c e p t i o n { s u p e r ( ) ; }

8
9

p u b l i c b y t e [ ] r e a d ( S t r i n g n o m b r e F i c h e r o , i n t numBytes , l o n g p o s ) {
try {
R a n d o m A c c e s s F i l e f = new R a n d o m A c c e s s F i l e ( n o m b r e F i c h e r o , " r " ) ;
f . se e k ( pos ) ;
int cuenta ;
b y t e [ ] b u f =new b y t e [ numB yte s ] ;
cu enta = f . read ( buf ) ;
f . close () ;
i f ( c u e n t a < numB yte s ) {
b y t e [ ] s h o r t b u f =new b y t e [ c u e n t a ] ;
f o r ( i n t i = 0 ; i < c u e n t a ; i ++) s h o r t b u f [ i ] = b u f [ i ] ;
return s h o r t b u f ;
}
e l s e return buf ;
}
catch ( Ex c ep tion e ) {
Sys te m . e r r . p r i n t l n ( " E r r o r " + e . g e t M e s s a g e ( ) + " e j e c u t a n d o r e a d s o b r e : " + n o m b r e F i c h e r o
);
return n u l l ;
}
}

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
30

1 El que sea sin estado quiere decir que no almacena informacin en memoria entre una invocacin y la siguiente, por lo que sus
operaciones deben ser idempotentes (que si se retransmiten no tienen ningn efecto adverso, ya que contienen todos los datos
necesarios). Un servidor sin estado tolera reinicios del servidor sin necesidad de que el cliente se entere.

pu b l i c void w r i t e ( S t r i n g nombreFichero , byte [ ] datos , long pos ) {


try {
R a n d o m A c c e s s F i l e f = new R a n d o m A c c e s s F i l e ( n o m b r e F i c h e r o , " rw " ) ;
f . se e k ( pos ) ;
f . write ( datos ) ;
f . close () ;
return ;
}
catch ( Ex c ep tion e ) {
Sys te m . e r r . p r i n t l n ( " E r r o r " + e . g e t M e s s a g e ( ) + " e j e c u t a n d o w r i t e s o b r e : " + n o m b r e F i c h e r o
) ; return ;
}
}

88
89
90
91
92
93
94
95
96
97
98
99
43

p u b l i c s t a t i c v o i d ma in ( S t r i n g a r g v [ ] ) {
i f ( Sys te m . g e t S e c u r i t y M a n a g e r ( ) == n u l l ) { Sys te m . s e t S e c u r i t y M a n a g e r ( new R M I S e c u r i t y M a n a g e r ( )
); }
try {
S e r v i d o r F i c h e r o s f i = new S e r v i d o r F i c h e r o s I m p l ( ) ; Naming . r e b i n d ( " / / 1 2 7 . 0 . 0 . 1 / S e r v i d o r F i c h e
ros " , f i ) ;
}
c a t c h ( E x c e p t i o n e ) { Sys te m . o u t . p r i n t l n ( " S e r v i d o r F i c h e r o s : " + e . g e t M e s s a g e ( ) ) ; }
}

63
64
65
66
67
68
69
70

93

Se pide:

Subpregunta 5.1 (0.3 puntos)


Observe que los mtodos no estn sincronizados, como en la ltima prctica. Explique si est
bien o est mal y por qu.
Est bien, ya que no hay variables compartidas, al ser un servidor sin estado. No
obstante, puede ocu- rrir que haya operaciones concurrentes sobre el mismo fichero, que
comparten, por ejemplo, el cursor que ponen (con seek). Ciertamente el que el mtodo sea
sincronizado elimina parte del problema, pero obsrvese que la mayora de las
aplicaciones hacen varias lecturas y escrituras, y la consistencia de las operaciones que
representan slo se puede garantizar con cerrojos externos.

Subpregunta 5.2 (1.2 puntos)


Implementar una operacin de borrado remoto de fichero. Use el mtodo delete () de la clase File ,
que tiene un constructor al que se le pasa el nombre del fichero.
1
2
3
4

p u b l i c i n t e r f a c e S e r v i d o r F i c h e r o s L E B e x t e n d s R e mote {
// . . . . .
p ub l i c boolean d e l e t e ( S t r i n g nombreFichero ) throws RemoteException ;
// . . . . .

p u b l i c c l a s s S e r v id or F i c h e r o sL EBIm pl e x t en d s Un ic a s tR e m o te Ob ject implements S e r vi do r F i c h erosL


EB {

1
2
3

p u b l i c Se r vido r F icher osLE BImpl ( ) throws RemoteException { super ( ) ; }

4
5
63
64
65
66
67
68
69
70
71
72
73
74

// . . . . .
p u b l i c boolean d e l e t e ( S t r i n g nombreFichero ) {
try {
F i l e f = new F i l e ( n o m b r e F i c h e r o ) ;
f . delete ();
return true ;
}
catch ( Exception e ) {
System . er r . p r i n t l n (
" E rror " + e . ge tMe s s age ( ) + " e j e c u t a n d o d e l e t e s o b r e : " + nombr e Fic he ro ) ;
return f al s e ;
}
}

18
19
33
34
22

// ...
S e r v i d o r F i c h e r o s L E B I m p l f i = new S e r v i d o r F i c h e r o s L E B I m p l ( ) ;
Naming . r e b i n d ( " / / 1 2 7 . 0 . 0 . 1 / S e r v i d o r F i c h e r o s " , f i ) ;
// ...

Subpregunta 5.3 (1.5 puntos)


Implementar un programa que mueva un fichero de una mquina a otra, es decir, que lo copie
de una a otra y luego lo borre de la primera (suponga que en todas ellas hay servidores
instalados). Se invocar con cuatro parmetros:
j a v a RMImv m a q u i n a O r i g e n f i c h e r o O r i g e n

maquinaDestino f i c h e r o D e s t i n o

Puede inspirarse en estos dos clientes. El primero lee un fichero remoto y lo saca por la salida
estndar:
33
34
3
4

impor t j a v a . i o . ;
impor t j a v a . r m i . ;
p u b l i c c l a s s R M Ile e {

5
21
22
23
24
25
26
27
28
29
30
16
29

p u b l i c s t a t i c v o i d ma in ( S t r i n g a r g v [ ] ) {
i f ( a r g v . l e n g t h ! = 2 ) { Sys te m . e r r . p r i n t l n ( " Uso : R M Ile e n o m b r e F i c h e r o nombre M a quina " ) ; Sys te m .
exit (1);}
try {
S t r i n g nombre = " / / " + a r g v [ 1 ] + " / S e r v i d o r F i c h e r o s " ;
S e r v i d o r F i c h e r o s f s = ( S e r v i d o r F i c h e r o s ) Naming . l o o k u p ( nombre ) ;
byte [ ] d a t o s ;
long pos = 0 ;
do {
d a t o s = f s . r e ad ( argv [0 ] , 1 0 2 4 , pos ) ;
Sys te m . o u t . w r i t e ( d a t o s ) ;
p o s += 1 0 2 4 ;
}

94

w h i l e ( d a t o s . l e n g t h == 1 0 2 4 ) ;
}
c a t c h ( R e m o t e E x c e p t i o n e ) { Sys te m . e r r . p r i n t l n ( " E x c e p c i o n r e m o t a : " + e . g e t M e s s a g e ( ) ) ; }
c a t c h ( E x c e p t i o n e ) { Sys te m . e r r . p r i n t l n ( " E x c e p c i o n : " + e . g e t M e s s a g e ( ) ) ; }

30
31
32
33

34
35

El segundo copia la entrada estndar en un fichero remoto:


15
16
17
18

impor t
impor t
impor t
impor t

java . io . File ;
java . io . FileInputStrea m ;
java . io . ;
j a v a . r mi . ;

5
6

p u b l i c c l a s s RMIesc {

p u b l i c s t a t i c v o i d ma in ( S t r i n g a r g v [ ] ) {
i f ( a r g v . l e n g t h ! = 2 ) { Sys te m . e r r . p r i n t l n ( " Uso : RMIesc n o m b r e F i c h e r o nombre M a quina " ) ; Sys te m .
exit (1);}
try {
S t r i n g nombre = " / / " + a r g v [ 1 ] + " / S e r v i d o r F i c h e r o s " ;
S e r v i d o r F i c h e r o s f s = ( S e r v i d o r F i c h e r o s ) Naming . l o o k u p ( nombre ) ;
B u f f e r e d R e a d e r b r = new B u f f e r e d R e a d e r ( new I n p u t S t r e a m R e a d e r ( Sys te m . i n ) ) ;
String l i ne = null ;
l i n e = br . read Line ( ) ;
long pos = 0 ;
while ( l i n e != n u l l ) {
l i n e += " \ n " ;
byte [ ] d a t a = l i n e . g e tByte s ( ) ;
f s . w r i t e ( argv [ 0 ] , data , pos ) ;
p o s += d a t a . l e n g t h ;
l i n e = br . read Line ( ) ;
}
}
c a t c h ( R e m o t e E x c e p t i o n e ) { Sys te m . e r r . p r i n t l n ( " E x c e p c i o n r e m o t a : " + e . g e t M e s s a g e ( ) ) ; }
c a t c h ( E x c e p t i o n e ) { Sys te m . e r r . p r i n t l n ( " E x c e p c i o n : " + e . g e t M e s s a g e ( ) ) ; }
}

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

}
import j a v a . io . ;
im port j a v a . rmi . ;
3
4

p u b l i c c l a s s RMImv {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
i f ( a r g v . l e n g t h != 4 ) {
S y s t e m . e r r . p r i n t l n ( " Uso : RMImv m a q u i n a O r i g e n f i c h e r o O r i g e n m a q u i n a D e s t i n o f i c h e r o D
estino" );
System . e x i t ( 1 ) ;
}
try {
S t r i n g nombreOrigen = " / / " + ar gv [ 0] + " / S e r v i d o r F i c h e r o s " ;
S t r i n g nombreDestino = " / / " + argv [ 2] + " / S er v i d o r F i c h e r o s " ;
S e r v i d o r F i c h e r o s L E B f s O r i g e n = ( S e r v i d o r F i c h e r o s L E B ) Naming . l o o k u p ( n o m b r e O r i g e n ) ;
S e r v i d o r F i c h e r o s L E B f s D e s t i n o = ( S e r v i d o r F i c h e r o s L E B ) Naming . l o o k u p ( n o m b r e D e s t i n o ) ;
byte [] datos ;
long pos = 0;
do {
d a to s=fs Or ig e n . read ( argv [ 1] , 10 2 4 , pos ) ;
f s D e s t i n o . w r i t e ( argv [ 3] , datos , pos ) ;
p o s+= 1 0 2 4 ;
}
w h i l e ( d a t o s . l e n g t h == 1 0 2 4 ) ;
fs Or igen . d e l e t e ( argv [ 1 ] ) ;
}
c a t c h ( R e m o t e E x c e p t i o n e ) { S y s t e m . e r r . p r i n t l n ( " E x c e p c i o n r e m o t a : "+ e . g e t M e s s a g e ( ) ) ; }
c a t c h ( E x c e p t i o n e ) { S y s t e m . e r r . p r i n t l n ( " E x c e p c i o n : "+ e . g e t M e s s a g e ( ) ) ; }
}

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
21
38
39
40
41
42
43
44
45

95

Pregunta 5 Sept 2010


Se quiere implementar un servicio RMI que diga y aprenda chistes. Tiene dos operaciones: pedir
chiste y aadir chiste. Los chistes se guardan en un fichero de acceso aleatorio. Cuando se pida un
chiste, se elige aleatoriamente uno del fichero. Cuando se aade un chiste, se hace al final.

Subpregunta 5.1 (1 punto)


Escriba una interfaz del servicio. Para simplificar, un chiste es un String de una sola lnea, sin
caracteres de control.
i m p o r t j a v a . r m i . R e mote ;
im port j a v a . rmi . Re mote E xc epti on ;
import ja v a . io . F i l e ;
4

p u b l i c i n t e r f a c e S e r v i d o r D e C h i s t e s e x t e n d s R e mote {
p u b l i c v o id aprender ( S t r i n g c h i s t e ) throws RemoteException ;
p ub l i c S t r i n g d e c i r ( ) throws RemoteException ;
}

82
83
84
85

Subpregunta 5.2 (1 punto)


Escriba un cliente que muestre tres chistes y luego almacene el chiste que a usted se le ocurra
(o cualquier frase). Las operaciones remotas se realizan en la mquina que se pasa como parmetro.
import
import
import
import

51
52
53
54
5

java . io . File ;
java . io . F ileInputStr ea m ;
java . io .;
j a v a . rmi . ;

public class ClienteDeChistes {

6
7

p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
i f ( a r g v . l e n g t h != 1 ) { S y s t e m . e r r . p r i n t l n ( " Us ar C l i e n t e D e C h i s t e s nombr e Maquina " ) ; S y s t e m
. exit (1); }
try {
S t r i n g nombr e = " / / " + a r g v [ 0 ] + " / S e r v i d o r D e C h i s t e s " ;
S e r v i d o r D e C h i s t e s s c = ( S e r v i d o r D e C h i s t e s ) Naming . l o o k u p ( nombr e ) ;
System . ou t . p r i n t l n ( sc . d e c i r ( ) ) ;
System . ou t . p r i n t l n ( sc . d e c i r ( ) ) ;
System . ou t . p r i n t l n ( sc . d e c i r ( ) ) ;
s c . a p r e n d e r ( " un e x ame n no e s un c h i s t e " ) ;
}
catch ( Exception e ) { e . pr intStackTrace ( ) ; }
}

140
141
142
143
144
145
146
147
148
149
150
151
152

Subpregunta 5.3 (1 punto)


Escriba la implementacin del servidor, sabiendo que:
Ficheros de acceso aleatorio:
Un fichero de acceso aleatorio se representa por la clase java . io . RandomAccessFile.
Un constructor toma dos parmetros: el nombre del fichero (ponga Chistes.txt) y el
modo de apertura (r o rw): RandomAccessFile(String filename , String mode).
Mtodos de inters son:
1
2
3
4
5

void seek ( long pos ) ;


long l e n g t h ( ) ;
St ri n g readLine ( ) ;
writeChars ( String s ) ;
void c l o s e ( ) ;

//
//
//
//
//

P o s i c i o n a r s e en e l b y t e pos
Saber su l o n g i t u d
Leer la s i g u i e n t e l n e a
E s c r i b e una c a d e n a
Cierra el f ic h er o

Nmeros seudoaleatorios:
Un generador de nmeros aleatorios se representa por la clase

java . util . Random.

Un constructor no necesita parmetros.


Y se pueden obtener nmeros aleatorios enteros de 0 a n-1 con:
1

int nextInt ( int n ) ;

96

java . io .;
j a v a . u t i l . Random ;
102 i m p o r t j a v a . r m i . ;
103 i m p o r t j a v a . r m i . s e r v e r . U n i c a s t R e m o t e O b j e c t ;
100 i m p o r t
101 i m p o r t

5
6

p u b l i c c l a s s S e r v i d o r D e C h i s t e s I m p l e x t e nd s U n i c a s t R e m o t e Ob j ec t im plem ents S e r v i d o r D e C h i s t e s {

p r i v a t e Random g e n e r a d o r ;
p r i v a t e RandomAccessFile f c ;

71
72
10

p ub l i c S er vido rDeChis tes Imp l ( ) throws RemoteException , FileNotFound Exception {


super ( ) ;
g e n e r a d o r = new Random ( ) ;
f c = new R a n d o m A c c e s s F i l e ( " C h i s t e s . t x t " , " rw " ) ;
}

75
76
77
78
79
16

p ub l i c s ynchroniz ed void aprender ( S t r i n g c h i s t e ) {


try {
fc . seek ( fc . length ( ) ) ;
/ / Lo e s c r i b e a l f i n a l
f c . w r i t e C h a r s ( c h i s t e +" \ n " ) ;
return ;
}
catch ( Exception e ) { e . pr intStackTr ace ( ) ; return ; }
}

35
36
37
38
39
40
41
42
25

public synchronized S t r i n g decir ( ) {


S t r i n g s= " No sQ
c chistes " ;
try {
long l e n g t h = fc . l e n g t h ( ) ;
i f ( l e n g t h == 0 ) r e t u r n s ; / / No hay nada
i n t p o s = g e n e r a d o r . n e x t I n t ( ( i n t ) l e n g t h / 2 ) ; / / Las c a d e n a s e n e l f i c h e r o s o n de d o s b y t e s
f c . s e e k ( pos 2 );
s= f c . r e a d L i n e ( ) ; / / Le e t r o z o de c h i s t e
s= f c . r e a d L i n e ( ) ; / / I n t e n t a l e e r uno e n t e r o
i f ( ( s == n u l l ) | | ( s . l e n g t h ( ) == 0 ) ) { / / s i no hay , m i r a a l p r i n c i p i o
fc . seek ( 0) ;
s= f c . r e a d L i n e ( ) ;
}
return s ;
}
catch ( Exception e ) { e . pr intStackTrace ( ) ; return s ; }
}

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
43

p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
i f ( S y s t e m . g e t S e c u r i t y M a n a g e r ( ) == n u l l ) { S y s t e m . s e t S e c u r i t y M a n a g e r ( new R M I S e c u r i t y M a n a g e r ( )
); }
try {
S e r v i d o r D e C h i s t e s s c h = new S e r v i d o r D e C h i s t e s I m p l ( ) ;
Naming . r e b i n d ( " / / 1 2 7 . 0 . 0 . 1 / S e r v i d o r D e C h i s t e s " , s c h ) ;
}
catch ( Exception e ) { e . printStackTrace ( ) ; }
}

31
32
33
34
35
36
37
38
39

97

Pregunta 5 (2,5 puntos) Jun 2011


Se quiere controlar el azimut (ngulo horizontal) y la altura (ngulo vertical) de un telescopio con
dos motores paso a paso y dos gonimetros. Cada uno de los motores y los gonimetros tiene un
procesador que ejecuta Java RMI. En los motores tenemos el servicio MotorPaP y en cada uno de
los gonimetros tenemos el servicio Goniometro, definidos como sigue:
impor t j a v a . r m i . Remote ;
impor t j a v a . r m i . R e m o t e E x c e p t i o n ;

3
86
87
88
89
90

55
56

p u b l i c i n t e r f a c e M otorPa P e x t e n d s Remote {
p u b l i c v o i d Mueve ( b o o l e a n a v a n z a ) thr ows R e m o t e E x c e p t i o n ;
/ / Mueve un pas o , a v a n z a n d o ( a v a n z a = t r u e ) o r e t r o c e d i e n d o ( a v a n z a = f a l s e )
/ / Se b l o q u e a h a s t a t e r m i n a r e l p a s o
}
impor t j a v a . r m i . Remote ;
impor t j a v a . r m i . R e m o t e E x c e p t i o n ;

3
p u b l i c i n t e r f a c e G o n i o m e t r o e x t e n d s Remote {
p u b l i c i n t Angulo ( ) thr ows R e m o t e E x c e p t i o n ;
155
/ / Mide e l a n g u l o e n m i c r o r a d i a n e s
156 }
153
154

Se pide implementar un programa con dos parmetros que permita controlar la posicin del
telescopio en microrradianes (la posicin final nunca puede ser exacta, ya que los motores son
discretos). Los parmetros son la altura y el azimut que se quiere que tenga el telescopio. Los URI de
los servicios son fijos:
//maltura/MotorPaP
//mazimut/MotorPaP
//galtura/Goniometro
//gazimut/Goniometro

Suponga que el paso del motor paso a paso es de 100 microradianes, luego la posicin final nunca
puede ser exacta. Implemente la solucin ms sencilla posible y explique brevemente como hacerla ms
rpida y con menos trfico en la red.
NOTA: Un motor paso a paso es un motor elctrico que cada vez que recibe una orden de movimiento gira
un
ngulo fijo,
llamado paso.
Una solucin sencilla y de cierta rapidez es sta:
r e p l i m p o r t j av a . rmi . ;
import j ava . rmi . s e r v e r . U ni cast R em ot e Obj ect ;
3
4 public
5
104
105

8
9
73
74
75
76
77
78
79
80
81

19
20
21
22
23

cl a s s ApuntaTelescopio {

s t a t i c MotorPaP mAzimut , m A l t u r a ;
s t a t i c Goniometro gAzimut , g A l t u r a ;
s t a t i c v o i d Apunta ( i n t azimut , i n t a l t u r a ) {
i n t azi , a l t , eaci , e a l t , paso = 100;
try {
do {
a z i = g A z i m u t . A n g u l o ( ) ; e a z i = a z i m u t a z i ;
a l t = g A l t u r a . A n g u l o ( ) ; e a l t = a l t u r a a l t ;
m A z i m u t . Mueve ( a z i m u t > a z i ) ;
m A l t u r a . Mueve ( a l t u r a > a l t ) ;
w h i l e ( ( Math . a b s ( e a z i ) < p a s o ) && ( Math . a b s ( e a l t ) < p a s o ) ) ;
} c a t c h ( R e m o t e E x c e p t i o n e ) { S y s t e m . e r r . p r i n t l n ( " E x c e p c i on r e m o t a
: "
+ e . getMessage ( ) ) ;
} c a t c h ( E x c e p t i o n e ) { S y s t e m . e r r . p r i n t l n ( " E x c e p c i on : "
+ e . getMessage ( ) ) ; }
}

98

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) {
i f ( S y s t e m . g e t S e c u r i t y M a n a g e r ( ) == n u l l )
S y s t e m . s e t S e c u r i t y M a n a g e r ( new R M I S e c u r i t y M a n a g e r ( ) ) ;
try {
MotorPaP m A z i m u t
= ( MotorPaP ) Naming . l o o k u p ( " / / m a z i m u t / MotorPaP
" );
MotorPaP m A l t u r a
= ( MotorPaP ) Naming . l o o k u p ( " / / m a l t u r a / MotorPaP
" );
G o n i o m e t r o g A z i m u t = ( G o n i o m e t r o ) Naming . l o o k u p ( " / / g a z i m u t / G o n i o
metro " ) ;
G o n i o m e t r o g A l t u r a = ( G o n i o m e t r o ) Naming . l o o k u p ( " / / g a l t u r a / G o n i o
metro " ) ;
Apunta ( I n t e g e r . p a r s e I n t ( args [ 0 ] ) , I n t e g e r . p a r s e I n t ( args [ 1 ] ) ) ;
} c a t c h ( R e m o t e E x c e p t i o n e ) { S y s t e m . e r r . p r i n t l n ( " E x c e p c i on r e m o t a :
"
+ e . getMessage ( ) ) ;
} c a t c h ( E x c e p t i o n e ) { S y s t e m . e r r . p r i n t l n ( " E x c e p c i on : "
+ e . getMessage ( ) ) ; }
}

80
81
82
83
84

85

86

87

88
89

34
35
36
43
44

Podra mejorarse haciendo que una hebra se encargue de cada motor. Tambin se
pueden ahorrar llamadas a los gonimetros midiendo el ngulo de un paso.

99

Pregunta 4 Dic 2012


En este ejercicio se debe modificar la prctica 11: Comunicaciones con RMI.

Subpregunta 4.1 (1,5 puntos)


Desarrollar un nuevo cliente que realice la copia de un fichero de nombre ficheroOrigen en el
fichero fichero- Destino teniendo en cuenta las siguientes consideraciones:

El fichero ficheroDestino no existe en el sistema de ficheros remoto.

No se deben crear operaciones adicionales en el servidor.

No se deben crear nuevos ficheros en la mquina en la que ejecuta el cliente.

Se deben identificar posible excepciones o errores, aunque no tratarlas. Si se detectan errores


se escribe un mensaje y se termina el programa.

import java . io . ;
i mpo r t j a v a . rmi . ;
91
92
93
94
95
96

97
98
99
100
101
102
103
104
18
57
58
59
60
61
62
63
64
65

p u b l i c c l a s s RMIcopia {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
i f ( a r g v . l e n g t h != 3 ) {
S y s t e m . e r r . p r i n t l n ( " Uso : RMIcopy n o m b r e F i c h e r o n o m b r e F i c h e r o nombreMaquina " ) ;
System . e x i t ( 1 ) ;
}
try {
S t r i n g nombre = " / / " + a r g v [ 2 ] + " / S e r v i d o r F i c h e r o s " ;
S e r v i d o r F i c h e r o s f s = ( S e r v i d o r F i c h e r o s ) Naming . l o o k u p ( nombre ) ;
byte [] datos ;
long pos = 0;
do {
d a t o s= f s . read ( argv [ 0] , 1 02 4 , pos ) ;
f s . w r i t e ( argv [ 1 ] , datos , pos ) ;
p o s+= 1 0 2 4 ;
}
w h i l e ( d a t o s . l e n g t h == 1 0 2 4 ) ;
}
catch ( RemoteException e ) {
S y s t e m . e r r . p r i n t l n ( " E x c e p c i o n r e m o t a : "+ e . g e t M e s s a g e ( ) ) ;
} catch ( Exception e ) {
S y s t e m . e r r . p r i n t l n ( " E x c e p c i o n : "+ e . g e t M e s s a g e ( ) ) ; }
}
}

Subpregunta 4.2 (1 punto)


Si el servidor falla durante su ejecucin, se podra quedar un fichero copiado parcialmente en el
servidor remo- to. Suponiendo que en el servidor incluye un mtodo remoto public void
delete(String nombreFichero);, modificar el programa desarrollado anteriormente, para intentar
resolver este problema, de forma que se escriba un mensaje de error y, adems, se deje el sistema de
ficheros del servidor como estaba antes de iniciar la copia.
157 i m p o r t
158 i m p o r t

java . io .;
j a v a . rmi . ;

106 p u b l i c
107
108
109
110
111
112
113

c l a s s RMIcopia42 {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g v [ ] ) {
i f ( a r g v . l e n g t h != 3 ) {
S y s t e m . e r r . p r i n t l n ( " Uso : RMIcopy n o m b r e F i c h e r o n o m b r e F i c h e r o nombreMaquina " ) ;
System . e x i t ( 1 ) ;
}
S e r v i d o r F i c h e r o s f s=n u l l ;
try {

100

S t r i n g nombre = " / / " + a r g v [ 2 ] + " / S e r v i d o r F i c h e r o s " ;


f s = ( S e r v i d o r F i c h e r o s ) Naming . l o o k u p ( nombre ) ;
byte [] datos ;
long pos = 0;
do {
d a t o s= f s . read ( argv [ 0] , 1 02 4 , pos ) ;
f s . w r i t e ( argv [ 1 ] , datos , pos ) ;
p o s+= 1 0 2 4 ;
}
w h i l e ( d a t o s . l e n g t h == 1 0 2 4 ) ;

114
115
116
117
118
119
120
19
82
83

}
catch ( RemoteException e ) {
S y s t e m . e r r . p r i n t l n ( " E x c e p c i o n r e m o t a : "+ e . g e t M e s s a g e ( ) ) ;
i f ( f s != n u l l )
try {
f s . d e l e t e ( argv [ 2 ] ) ;
} c a t c h ( E x c e p t i o n e2 ) { }
} catch ( Exception e ) {
S y s t e m . e r r . p r i n t l n ( " E x c e p c i o n : "+ e . g e t M e s s a g e ( ) ) ; }
i f ( f s != n u l l )
try {
f s . d e l e t e ( argv [ 2 ] ) ;
} c a t c h ( E x c e p t i o n e2 ) { }

84
85
86
87
88
89
90
91
92
93
94
95
96

97
98

101

Vous aimerez peut-être aussi