Examen de Programación Concurrente: Septiembre 2008 David Fernández Amorós 1. Se desea resolver el problema del productor-consumidor usando semáforos para enviar y recibir. Hay un solo emisor y un solo receptor y las operaciones que realizan son enviar y recibir respectivamente. La información que se envía se pasa a través de una variable compartida, en este caso valor, por lo que el buffer de comunicación tiene longitud uno. El protocolo es síncrono, esto quiere decir que el primer proceso, ya sea el productor o el consumidor que llega a la cita tiene que esperar al otro. Rellene el hueco en el pseudocódigo del procedimiento recibir para lograr este objetivo. (2pt) var primero : boolean; mutex : semáforo; segundo : semáforo; valor : entero; procedure enviar(v:entero); begin wait(mutex); valor := v; if primero then begin primero := false; signal(mutex); wait(segundo); signal(mutex); end else begin primero := true; signal(segundo); end end; procedure recibir(var v: entero); begin wait(mutex); if primero then ... else begin primero := true; v := valor; signal(segundo); end end proceso productor; var i: entero; begin for i := 1 to 10 do enviar(i) end; proceso consumidor; var i,v : entero; begin for i := 1 to 10 do begin recibir(v); writeln(v) end end; begin primero := true; init(mutex,1); init(segundo,0); cobegin productor; consumidor; coend end. El texto que corresponde a los puntos suspensivos es el siguiente: begin primero := false; signal(mutex); wait(segundo); v := valor; signal(mutex); end 2. Un lobo y un cordero acudieron a la orilla del mismo río, empujados por 1 Examen de Programación Concurrente: Septiembre 2008 David Fernández Amorós la sed. El lobo estaba arriba del curso del río y el cordero mas abajo. El lobo imaginó un simple pretexto a fin de devorarlo. Así, dijo el lobo: – Cordero, me enturbias el agua. – Pero, ¿cómo puedo hacer eso que dices, lobo, si estoy más abajo? Viéndose el lobo burlado, insistió: – Hace seis meses insultaste a mis padres. – ¡ Pero por aquel entonces yo ni siquiera había nacido ! – contestó el cordero. Dijo entonces el lobo: – Entonces sería tu hermano. De todas formas serás mi cena. Y se lo comió. Desde entonces, los corderos han modificado su patrón de conducta para no quedarse a solas con un lobo. Un lobo va a beber al río cuando tiene sed y bebe. Si hubiera un cordero solo, entonces se lo comería y se iría, pero si hay varios corderos no se come a ninguno y se va, hasta que vuelve a tener sed, momento en el cual se repite la misma historia. A un lobo no le afecta que haya otros lobos en el río. El comportamiento de los corderos también implica que periódicamente tienen sed y quieren beber en el río. Después de beber se van, hasta que vuelven a tener sed. a. Piense una estrategia para los corderos que garantice su supervivencia, es decir, que nunca se va a quedar solo un cordero en el río en presencia de uno o varios lobos. Describa brevemente dicha estrategia. (2pt). Para los lobos no hace falta ninguna estrategia, la cuestión recae por entero en los corderos. Lo más sencillo es considerar el río como un recurso. Necesitaremos un protocolo de entrada al río y otro de salida. A la hora de entrar hay dos posibilidades: La primera es que haya más de un cordero bebiendo. En ese caso podemos entrar a beber sin más. La segunda es que no haya ninguno bebiendo. Este caso se subdivide en otros dos, dependiendo de que haya otro cordero esperando para acercarse al río o no. Si no hay otro corderos esperando, entonces toca esperar, pero si hay otro cordero esperando para acceder al río, los dos entran al río al mismo tiempo. Además, 2 Examen de Programación Concurrente: Septiembre 2008 David Fernández Amorós si hay algún cordero esperando salir del río, después de entrar es el momento para avisarlo de que ya puede irse. En el caso principal no hay que considerar la posibilidad de que sólo haya un cordero bebiendo porque esa estrategia no es robusta, ya que en cualquier momento podría aparecer un lobo y comerse al cordero. Para el protocolo de salida, la situación es similar. Si hay más de dos corderos bebiendo, entonces la salida es posible directamente. Si sólo hay dos corderos, hay dos posibilidades: Si no hay nadie esperando para salir, el cordero se queda a la espera. Si ya hay uno esperando, entonces se le avisa y salen los dos a la vez. b. Se pide simular el comportamiento de un grupo de diez lobos y diez corderos mediante un programa concurrente escrito en pseudocódigo, utilizando los tipos de proceso lobo y cordero, de forma que el comportamiento de los lobos se ajuste a lo descrito en el enunciado y el de los corderos se ajuste tanto a lo indicado en el enunciado como a la estrategia de supervivencia desarrollada en el punto anterior. Puede elegir para la implementación entre semáforos y monitores (no una mezcla de ambos). Para seguir la simulación, los procesos deben emitir unos mensajes por pantalla que expliquen razonablemente lo que están haciendo. La solución debe contar con unas variables y estructuras de datos que permitan resolver razonablemente el problema. Es vital que se respete escrupulosamente la exclusión mutua en el manejo de las variables compartidas. Además, se prestará atención para evitar situaciones de interbloqueo y espera activa. Asimismo, se deberar procurar alcanzar un alto grado de concurrencia entre los procesos y una correcta sincronización entre los mismos. Si se identifica un problema-tipo, debe mencionarse explícitamente (incluyendo, en su caso, la prioridad más adecuada) antes de proceder con el pseudocódigo. Es muy importante que el comportamiento de los procesos se ajuste al enunciado, tanto en los aspectos específicamente concurrentes como en los demás. (6pt) En primer lugar, hay que decir que este problema no corresponde a ninguno de los dos problemas-tipo estudiados en la asignatura. Es cierto que tiene un lejano parecido con el de los lectores y escritores, pero también hay sutiles diferencias que hacen que la solución de aquel no pueda adaptarse fácilmente a este. Una solución empleando un monitor es la siguiente: 3 Examen de Programación Concurrente: Septiembre 2008 David Fernández Amorós delay(salir); writeln(’Soy el cordero ’,id, ’ y salgo del rio’); dentro := dentro - 1; end; program lobosycorderos; monitor rio; export corderoEntrar, corderoSalir, loboEntrar, loboSalir; begin dentro := 0; end; var entrar, salir : condition; dentro : integer; procedure loboentrar(id: integer); begin writeln(’Soy el lobo ’,id, ’ y entro a beber en el rio’); if dentro = 1 then writeln(’Soy el lobo ’,id, ’ y me como al cordero. Estrategia incorrecta’); end; procedure lobosalir(id : integer); begin writeln(’Soy el lobo ’,id, ’ y salgo de beber en el rio’); end; procedure corderoEntrar(id:integer); begin writeln(’Soy el cordero ’,id, ’ y tengo ganas de beber’); resume(entrar); if dentro = 0 then delay(entrar); writeln(’Soy el cordero ’,id, ’ y entro al rio’); dentro := dentro + 1; resume(salir); end; procedure corderoSalir(id: integer); begin writeln(’Soy el cordero ’,id, ’ y quiero salir del rio’); resume(salir); if dentro = 2 then process type lobo(id : integer); begin repeat rio.loboEntrar(id); rio.loboSalir(id); forever end; process type cordero(id : integer); begin repeat rio.corderoEntrar(id); rio.corderoSalir(id); forever; end; var lobos : array [1..10] of lobo; corderos : array [1..10] of cordero; bucle : integer; (* Programa principal *) begin cobegin for bucle:= 1 to 10 do begin lobos[bucle](bucle); corderos[bucle](bucle); end; coend; end. 4