Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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();
}
}
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
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();
}
}
}
}
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.
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.
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.
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.
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.
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).
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.
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.
10
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
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.
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;
}...
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.
# 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
15
16
Word
Word
1
0
$1 SWAP
Local,
$1
Cerrojo BZ
Region cr
tica
CLEAR Local
...
MOVI
#1,
Cerrojo Resto
del proceso
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
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
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.
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
- - Process B
...
region Y do
...
region X do
...
end region
...
end region
...
20
0..1;
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
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 ;
}
/
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 ;
}
23
1
2
3
4
5
6
7
8
9
10
11
12
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
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);
}
****
---****
****
****
---****
****
****
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
PeriodoProductor 1000000),
****
---****
---****
****
****
****
****
_REENTRANT.
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.
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.
int pesoEnPuente = 0;
int nVehiculos
= 0;
int nAmbulanciasEsperando = 0;
static final int pesoMaximo = 15000;
static final int nVehiculosMaximo = 10;
/////////////////////////////////////////////////////
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.
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).
Sept 2007
Pregunta 4 (3 puntos)
Jun 2008
Pregunta 4
/* 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. */
// Lee el reloj
Jun 2009
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 ) )
}}
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
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
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
38
39
43
44
45
public
{
43
44
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
40
41
42
43
44
45
46
47
40
48
49
50
51
52
53
54
48
49
15
*
*
*
*
*
24
25
26
27
28
29
30
31
32
16
10
11
12
13
14
15
23
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 ) ;
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
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;
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
<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>
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 -
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.
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);
}
}
}
}
10
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
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
protected S t r i n g nombreFichero ;
ReproductorSonido.java
/ / 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
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
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
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
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
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
5
6
7
8
9
10
11
12
13
14
EscribeConSonido.java
3
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
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) {
18
19
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
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
25
26
15
16
17
18
19
20
21
22
21
22
19
20
21
22
23
24
25
26
27
28
29
30
31
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
<unistd.h>
<stdio.h>
<stdlib.h>
<signal.h>
29
30
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
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 " ) ;
}
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
pcsem.c
#define _REENTRANT
#include
#include
#include
#include
#include
#include
<stdio.h>
<string.h>
<errno.h>
<pthread.h>
<semaphore.h>
<stdlib.h>
33
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
35
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
./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
./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
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
./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
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.
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.
46
<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
*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
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
50
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
54
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
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).
57
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
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
}
}
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
----
63
---****
---****
---****
---****
---****
---****
---****
---****
---****
---****
****
****
****
****
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:
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
----
Productor 1000
produce 1004
----
Productor 1000
produce 1005
65
---****
---****
---****
---****
---****
****
****
****
****
****
****
****
****
****
****
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
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
68
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
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{}}
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
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
13
14
15
16
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
3
4
5
6
7
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
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
/ / 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
impor t j a v a . u t i l . V e c t o r ;
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
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
java.io.File;
java.io.FileInputStream;
java.rmi.*;
java.rmi.server.UnicastRemoteObject;
java.util.Map;
java.util.HashMap;
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:
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
ServidorFicherosImpl.java
import
import
import
import
java.io.File;
java.io.RandomAccessFile;
java.rmi.*;
java.rmi.server.UnicastRemoteObject;
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.*;
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
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
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
92
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 ;
}
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.
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:
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 ;
// . . . . .
1
2
3
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 ) ;
// ...
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
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
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
51
52
53
54
5
java . io . File ;
java . io . F ileInputStr ea m ;
java . io .;
j a v a . rmi . ;
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
//
//
//
//
//
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
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
75
76
77
78
79
16
35
36
37
38
39
40
41
42
25
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
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
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 ( ) ) ; }
}
}
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
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