Pract 2
Pract 2
Pract 2
ya que se realiza a travs de memoria comn, sin embargo, garantizar la independencia de las hebras, mediante los mecanismos de sincronizacin apropiados, resulta ms complicado.
# -*- coding: iso-8859-15 -*import threading # Variable cerrojo cerrojo = threading.Lock() # Clase A1 class A1 (threading.Thread): # Constructor def __init__(self, nombre, siesta, args): Thread.__init__(self) self.setName(nombre) self._siesta = siesta # Metodo run def run (self): print 'Hola, soy %s' % this.getName() if self._siesta > 0: cerrojo.acquire() else: print 'Me fastidiaron la siesta!'
2. En otro lugar creamos una instancia de la clase denida. De esta forma se crea un objeto hebra (una instancia de una subclase de Thread). 3. Llamamos al mtodo start para la instancia creada. Esta llamada hace que el mtodo run se ejecute como una nueva hebra.
# Variable cerrojo cerrojo = threading.Lock() # Clase A1 class A1 (threading.Thread): # Constructor def __init__(self, nombre, siesta, args): threading.Thread.__init__(self) self.setName(nombre) self._siesta = siesta # Metodo run def run (self): print 'Hola, soy %s' % self.getName() if self._siesta > 0: cerrojo.acquire() else: print 'Me fastidiaron la siesta!' a1 = A1("Hebra_01", 0, []) a1.start()
Normalmente podemos ordenar un poco el cdigo para lanzar varias instancias de la hebra:
# -*- coding: iso-8859-15 -*import threading # Clase A1 class A1 (threading.Thread): # Constructor def __init__(self, nombre, siesta, cerrojo, args): threading.Thread.__init__(self) self.setName(nombre) self._siesta = siesta self._cerrojo = cerrojo # Metodo run def run (self): print 'Hola, soy %s' % self.getName() if self._siesta > 0: self._cerrojo.acquire() else: print 'Me fastidiaron la siesta!'
# Funcion main del programa if __name__== "__main__": # Variable cerrojo cerrojo = threading.Lock() lista = [] # Que pasa si en lugar de 0 pasamos un 1? for i in range(100): lista.append(A1("Hebra_%.2d" % i, 0, cerrojo, [])) for a in lista: a.start()
clase A metodo() Escribir("soy A") clase B metodo() Escribir("soy B") clase C, hereda de A y B c es instancia de C c.metodo()
qu debe hacer c.metodo()? C++ lo resuelve en tiempo de compilacin, pero Python es interpretado, por lo que no puede resolverlo en tiempo de compilacin. Lo que hace es bastante simple: Lleva a cabo una bsqueda en profundidad Si tenemos esta jerarqua:
Buscar en la clase E Si no est, buscar en C Si no est, buscar en B Si no est, buscar en A Si no est, buscar en D Si no est, buscar en B Si no est, buscar en A Si no est, enviar una excepcin
# Clase B1 class B1: def __init__(self): print 'Hola' # Clase A1 class A1 (threading.Thread, B1): # Constructor def __init__(self, nombre, siesta, cerrojo, args): # Llamada al primer constructor B1.__init__(self) # Llamada al segundo constructor threading.Thread.__init__(self) self.setName(nombre) self._siesta = siesta self._cerrojo = cerrojo # Metodo run def run (self): print 'Hola, soy %s' % self.getName() if self._siesta > 0: self._cerrojo.acquire() else: print 'Me fastidiaron la siesta!'
Normalmente, si los mtodos de B1 son pblicos, podemos implementar un objeto de la clase B1 y pasrselo al constructor, de forma que esta clase pueda acceder a los mtodos de la clase B1 a travs del objeto creado. En la mayora de los casos, suele ser una solucin ms elegante que la implementacin basada en herencia mltiple.
Existen dos formas de utilizarlas, el primero es pasar como argumento una funcin, la cual se ejecutar simtricamente con respecto a otras hebras del proceso. La otra, ms comn, es extender la clase, y en concreto, el mtodo run (tal y como se ha visto en el apartado anterior). Sus mtodos son los siguientes:
start() Lanza la hebra. Internamente llama al mtodo run. run() Indica el cdigo que se ejecutar cuando se lance la hebra. join() Espera a que la hebra termine, salvo cuando hay algn temporizador (timeout). getName() Devuelve el nombre de la hebra. setName() Cambia el nombre de la hebra. isAlive() Devuelve si la hebra est viva (ejecutndose) o no. isDaemon() Devuelve el ag de demonio. setDaemon() Activa el ag de demonio de la hebra. Un programa en Python puede
salir solamente cuando no hay hebras de demonio en su interior.
Lock Esta clase implementa un mtodo sencillo de cerrojo (se puede ver un ejemplo en el cdigo
anterior). Solamente tiene dos mtodos: Tiene un parmetro opcional.
acquire( blocking = 1 ) Adquiere un cerrojo, bloqueando el sistema o desbloquendolo. release() Abre un cerrojo. RLock Esta clase implementa un cerrojo reentrante. Es igual que el sistema de la clase Lock,
salvo que este cerrojo tiene incorporado un mtodo para controlar algoritmos recursivos. Solamente tiene dos mtodos:
release() Abre un cerrojo. Condition Una variable condicin est asociada siempre con algn tipo de cerrojo (el cual se
le suele pasar al constructor de la clase, siendo de tipo Lock o RLock). Tiene los siguientes mtodos:
acquire( *args ) Adquiere un cerrojo. release() Abre un cerrojo. wait( timeout ) Espera hasta que haya alguna noticacin o se pase el tiempo indicado
en la funcin. Este mtodo abre el cerrojo y espera un notify() o un notifyAll(). debe ser llamado slo cuando esta hebra haya adquirido el cierre.
notify() Despierta una hebra que est esperando esta condicin (si la hay). Este mtodo notifyAll() Despierta a todas las hebras que estn esperando esta condicin. Semaphore Implementa un semforo, tiene dos mtodos: acquire y release.
Event Implementa un sistema sencillo de eventos entre hebras. Los mtodos que tiene asociados
son los siguientes:
isSet() Devuelve true si el ag interno es verdadero. set() Cambia a true el ag interno. clear() Cambia a false el ag interno. wait() Espera hasta que el ag interno sea true. Timer Esta clase es una hebra de ejemplo que extiende a la clase Thread. Ejecuta una funcin
despus de que pase un tiempo:
def hola(): print "hola mundo!" t = Timer(30.0, hola) t.start() # tras 30 segundos aparecer\'a en pantalla: "hola mundo!"
Para utilizar cualquiera de estas clases ha de importarse el paquete threading. Este paquete es tambin un objeto con unos mtodos asociados. Por ejemplo, podemos conocer el nmero de hebras que se estn ejecutando en este momento escribiendo:
threading.activeCount()
La documentacin detallada de todos estos mtodos se encuentra en: http://docs.python.org/lib/module-threading.html
Podemos tener dos tipos de Sprite en un juego: 1. Dinmicos: se referirn a todos los personajes y animaciones. 2. Estticos: se referirn a todos los elementos del juego que no sean el fondo de la pantalla y que no se muevan a lo largo del juego. Las imgenes de los Sprites suelen tener una parte que es transparente, por ello, se marca mediante el canal llamado: Canal Alfa, un buer que nos indica qu partes de la imagen son transparentes y cuales opacas. Normalmente los programas de dibujo y tratamiento de imagen suelen asociar el canal alfa mostrando un fondo de cuadros y la imagen sobre este fondo, indicando que la imagen en esa parte es transparente o translcida. La otra forma de hacer esto mismo, es utilizar un color key, o color base, el cual se utiliza para marcar la transparencia, para ello, basta con que usemos un determinado color en RGB para marcar la transparencia de todas las imgenes. Por tanto, estos Sprites suelen tener fondos chillones ya que es poco probable que se encuentre ese color en la imagen.
import os, sys import pygame from pygame.locals import * if not pygame.font: print 'Advertencia!, estilos y letras desactivadas.' if not pygame.mixer: print 'Advertencia!, sonidos desactivados.'
Podemos inicializar la librera y crear una ventana en la pantalla simplemente aadiendo esto en nuestro programa principal:
pygame.init() screen = pygame.display.set_mode((468, 60)) # Resoluci\'on de la ventana (tamagno) pygame.display.set_caption('Nombre de la ventana') pygame.mouse.set_visible(0) # Oculta el raton (si se comenta, aparece el raton)
1 Normalmente debemos usar una funcin diferente de carga para el fondo que para cualquier Sprite, ya que en este caso no tendremos un color base ni un canal alfa para transparencia. Cargar una imagen en Pygame es sumamente sencillo:
def carga_fondo(name, colorkey=None): fullname = os.path.join('data', name) try: image = pygame.image.load(fullname) # Intenta cargar la imagen except pygame.error, message: # Si no puede muestra un mensaje print 'No se puede cargar la imagen:', name raise SystemExit, message image = image.convert() # Convierte la imagen al formato propio para visualizar return image, image.get_rect() # Devuelve la imagen y su tamagno
def carga_imagen(name, colorkey=None): fullname = os.path.join('data', name) try: image = pygame.image.load(fullname) # Intenta cargar la imagen
10
except pygame.error, message: # Si no puede muestra un mensaje print 'No se puede cargar la imagen:', name raise SystemExit, message image = image.convert() # Convierte la imagen al formato propio para visualizar if colorkey is not None: # Si se le dice que hay un color base if colorkey is -1: # Si es -1, lo coge a partir del primer pixel colorkey = image.get_at((0,0)) image.set_colorkey(colorkey, RLEACCEL) # Activa el flag con el color base return image, image.get_rect() # Devuelve la imagen y su tama\~no
Podemos utilizar esta misma funcin para la carga de fondos, basta con pasar solo un parmetro a la funcin.
# Divide la imagen en subimagenes: def divide_imagen(image, rect): subimage = image.subsurface(rect) return subimage, rect # Devuelve la imagen y su tamagno
Con esto no se crea una nueva imagen, si no que se referencia sobre la anterior, siendo ms eciente (ya que no ocupamos tanta memoria) y ms ptimo. Para girar una imagen podemos usar este cdigo:
# Damos la vuelta a la imagen para que mire al otro lado img = pygame.transform.flip(imagen[0], 1, 0)
Donde imagen[0] es la imagen obtenida cargndola previamente de un chero con la funcin anterior. Para que en el juego, todo vaya a la velocidad que debe ir, y no dependa de la mquina en la que lo ejecutemos (si tenemos un ordenador muy potente, es capaz de ejecutar el cdigo ms
11
rpido, y por tanto de dibujar ms fotogramas por segundo), lo que se suele hacer es incorporar un reloj en el sistema, de forma que cada Sprite sea capaz de dibujarse en una posicin concreta cuando le toca. En Pygame, tenemos un reloj que hace esto por nosotros:
clock = pygame.time.Clock() # Definimos un reloj while 1: clock.tick(60) # Este bucle no ira m\'as rapido de 60 fps
Estas hojas suelen montarse para no estar trabajando con cientos (o a veces miles) de imgenes. De hecho, las animaciones de los personajes suelen venir dadas de un lado nicamente, debido a que se pueden invertir (la operacin se conoce como ip ) respecto al eje que se desee. Normalmente, tambin se suele soportar las rotaciones con un ngulo arbitrario, por lo que si la imagen tiene una vista superior, una nica imagen nos sirve para todos los ngulos en el juego.
background = carga_imagen('escenario.jpg')
12
spritesheets1 = carga_imagen('spritesheet.png', -1) # A\~nadimos una animacion: anim0 = [] anim0.append(divide_imagen(spritesheets1[0], pygame.Rect(11, 5, 45-11, 93-5))) # Dibujamos en el buffer de pantalla: screen.blit(background[0], (0, 0)) # Damos la vuelta a la imagen para que mire al otro lado img = pygame.transform.flip(anim0[0][0], 1, 0) # Unimos la imagen a la pantalla, en la coordenada 10, 75 screen.blit(img, (10, 75)) # Volcamos el buffer de pantalla (screen) en la pantalla pygame.display.flip()
Para facilitar la carga de las animaciones se puede utilizar bucles siempre y cuando cada animacin del personaje tenga el mismo tamao y estn en posiciones equidistantes en la hoja de Sprites. En otro caso hay que ajustar a mano cada uno de las animaciones o hacer una utilidad que nos devuelva la posicin y el tamao de cada una de estas subimgenes.
13
Da igual que haya varios villanos. Tendremos una nica clase para ellos, ya que comparten grcos y el modo de actuar (su comportamiento). Por ejemplo, si tenemos un Sprite esttico para dibujar piedras y rboles esta podra ser su clase:
class Objeto(pygame.sprite.Sprite): """Este es un sprite del escenario""" def __init__(self, image): pygame.sprite.Sprite.__init__(self) # Carga la imagen y tambien su posicion y tamagno self.image, self.rect = load_image(image, -1) def update(self): "Actualiza el Sprite"
Incluso cuando el Sprite no haga nada, conviene extender el mtodo update. Cada Sprite puede pertenecer a un grupo, lo que facilita una divisin conceptual. Por ejemplo, podemos tener una grupo de Sprites que sean Escenario, e introducir tanto Sprites estticos como dinmicos en l. Tambin podramos tener otro grupo que fuera Enemigos, y otro llamado Personaje. Un mismo Sprite puede estar en varios grupos. Los mtodos de un Sprite, por tanto, son los siguientes:
update(*args) Controla el comportamiento del Sprite add(*groups) Aade el Sprite a un grupo remove(*groups) Elimina el Sprite de un grupo kill() Elimina el Sprite de todos los grupos alive() Devuelve verdadero si el Sprite est en algn grupo groups() Devuelve la lista de grupos donde est el Sprite
Los mtodos de la clase Group son los siguientes:
sprites() Una lista de todos los Sprites de este grupo copy() Duplica el grupo add(*sprites) Aade Sprites del grupo remove(*sprites) Elimina Sprites del grupo has(*sprites) Comprueba que existan todos los Sprites en el grupo draw(surface) Dibuja todos los Sprites
14
update(*args) Llama al mtodo update de todos los Sprites clear(dest,bg) Dibuja un fondo sobre los Sprites (los borra) empty() Elimina todos los Sprites
for event in pygame.event.get(): # Se leen los eventos if event.type == QUIT: # Si se pulsa el boton de cerrar ventana se sale exit(1) elif event.type == KEYDOWN and event.key == K_ESCAPE: # Tecla Escape exit(1)
Para un ejemplo completo de una demo de un juego, mirar el chero pg3.py. Toda la documentacin de la librera y algunos tutoriales se pueden encontrar en la siguiente direccin: http://www.pygame.org/docs/ Con la funcin:
Para evitarlo, se puede situar el personaje sobre la esquina inferior izquierda restando a cada operacin blit el tamao del rectngulo en el eje y, tal y como muestra este cdigo:
15
# Si la lista de animaciones es vacia, entonces pon la animacion 0 if self.listanim != []: anim = self.listanim[-1] else: anim = self.esperando # Damos la vuelta a la imagen para que mire al otro lado img = pygame.transform.flip(anim[self.frame][0], self.lado, 0) # Unimos la imagen a la pantalla, restamos en el eje y para que este en el suelo pos = pygame.Rect(self.rect[0], self.rect[1] - anim[self.frame][1][3], self.rect[2], screen.blit(img, pos)
Una vez que queremos introducir cualquier animacin basta con introducirla en la lista de animaciones. Habr animaciones que tengan mxima prioridad (como cuando el personaje muere) y otras que debern nalizarse para comenzar una nueva animacin. Esto es sencillo de ejecutar, ya que tenemos un bucle recorriendo los fotogramas. Si la prioridad es pequea, cuando el fotograma llega al nal se elimina la animacin de la lista y se coge la siguiente animacin. En otro caso, no se espera a nalizar los fotogramas de la animacin sino que se ejecuta directamente la siguiente animacin, esto lo podemos ver en este trozo de cdigo en la funcin update:
def update(self): """Anima el personaje""" # Si la lista de animaciones es vacia, entonces pon la animacion 0 if len(self.listanim) > 0: anim = self.listanim[-1] else: anim = self.esperando # Condiciones de muerte: if self.rect[0] < 10 and anim != self.muriendo: self.listanim = [self.muriendo] self.frame = 0 # Si llega al final arriba, muerte, maxima prioridad if self.suelomuerte < 300 and self.rect[0] > 300 and anim != self.muriendo: self.listanim = [self.muriendo] # Sustituye toda la lista de animaciones self.frame = 0 # Si llega al final abajo, muerte, maxima prioridad if self.suelomuerte >= 300 and self.rect[0] > 150 and anim != self.muriendo: self.listanim = [self.muriendo] # Sustituye toda la lista de animaciones self.frame = 0 self.clock = self.clock + 1
16
# Ve al siguiente frame, a 4 fps if self.clock > 4: self.clock = 0 self.frame = self.frame + 1 # Aumenta en uno el fotograma if anim == self.corriendo: if self.lado == 0: self.rect = self.rect.move(5, 0) # Mueve a dcha else: self.rect = self.rect.move(-5, 0) # Mueve a izq # Si se acabo la animacion, saca la animacion de la lista if self.frame >= len(anim): if anim != self.muriendo: self.frame = 0 if len(self.listanim) > 0: self.listanim.pop() if anim == self.vuelta: self.lado = not self.lado # Cambiamos el lado anim = self.esperando else: self.frame = len(anim) - 1
Ejercicio propuesto:
Aade una nueva animacin al personaje cuando se pulsa la tecla de la echa arriba (K_UP).
17
Para poder mantener la coherencia de los datos que estn sujetos a un acceso concurrente, no se puede utilizar, dentro del texto de los procedimientos, ninguna variable declarada fuera del monitor. Lo primero para crear un monitor en Python es crear una clase que contendr el cdigo que gestiona un proceso completo. Supongamos que lo que queremos hacer es un programa que sume de uno en uno hasta llegar a 5.000. Para ello crearemos 5 hebras y la siguiente clase:
# Clase Contador class Contador: """ Clase contadora """ def __init__(self, inicial): self.actual = inicial def inc(self): self.actual = self.actual + 1 def dec(self): self.actual = self.actual - 1 def valor(self): return self.actual
El problema que tenemos con esta clase, es que si desde cualquier hebra llamamos a uno de sus mtodos, no nos aseguramos de que se ejecute nicamente uno de sus mtodos al mismo tiempo. Para asegurarnos, podemos utilizar un objeto Condition.
18
esperar al menos hasta que la hebra que invoc notify libere el cerrojo, bien por la ejecucin de una llamada a wait, o bien por la salida del monitor. La hebra sealada no tiene prioridad alguna para ejecutarse en el monitor. Puede ocurrir que, antes de que la hebra sealada pueda volver a ejecutarse, otra hebra adquiera el cerrojo del monitor. La invocacin al mtodo notifyAll produce el mismo resultado que que una llamada a notify por cada hebra bloqueada en el conjunto de wait: todas las hebras bloqueadas pasan al estado preparado. La forma de crear la variable condicin es la siguiente:
# Clase A1 class A1 (threading.Thread): """ Clase que hace uso del contador """ def __init__(self, cnt, cond, nm): # Llamada al segundo constructor threading.Thread.__init__(self) self.contador = cnt self.condicion = cond self.nombre = nm def run (self): # Metodo run for i in range (1000): self.contador.inc() v = self.contador.valor() print 'Soy %s' % self.nombre print 'mi contador vale: %d' % v # Funcion main del programa if __name__== "__main__": # Variable cerrojo cerrojo = threading.Lock() condicion = threading.Condition(cerrojo) contador = Contador(0) lista = [] for i in range(5): lista.append(A1(contador, condicion, "Hebra %d" % i)) for a in lista: a.start()
Ejercicio propuesto:
Modica la clase Contador para que no se pueda entrar al cdigo de dos de sus mtodos al mismo tiempo.
19
# Consume un elemento cv.acquire() while not un_elemento_disponible(): cv.wait() coge_un_elemento_disponible() cv.release() # Produce un elemento cv.acquire() crear_un_elemento_disponible() cv.notify() cv.release()
Hay que tener en cuenta que adems de proteger los mtodos del monitor, hay que proteger tambin los mtodos que acceden a la animacin de los personajes ya que si no podemos ocasionar una inconsistencia en los datos (o que el programa salga debido a cualquier error). Tambin debemos considerar que los personajes van a actuar como monitores, por tanto, es recomendable protegerlos.
20
self.image, self.rect = fsprites.carga_imagen('mercante.png', -1) self.cerrojo = threading.Lock() # Ahora necesitamos un cerrojo para que no se llamen self.lado = lad self.listanim = [] self.clock = 0 self.frame = 0 self.esperando = fsprites.extrae_animacion(self.image, (7, 56, 205-7, 103-56)) self.regateando0 = fsprites.extrae_animacion(self.image, (91, 0, 264-91, 50-0)) self.regateando1 = fsprites.extrae_animacion(self.image, (1, 107, 264-1, 152-107)) self.soltando = fsprites.extrae_animacion(self.image, (230, 4, 345-230, 48-4)) self.cogiendo = fsprites.extrae_animacion(self.image, (224, 57, 372-224, 103-57)) # Movemos el mu\~neco en la posicion correcta: self.rect = self.rect.move(x, y) def update(self): """Anima el personaje""" self.cerrojo.acquire() # Si la lista de animaciones es vacia, entonces pon la animacion 0 if len(self.listanim) > 0: anim = self.listanim[-1] else: anim = self.esperando self.clock = self.clock + 1 # Ve al siguiente frame, a 8 fps if self.clock > 8: self.clock = 0 self.frame = self.frame + 1 # Aumenta en uno el fotograma # Si se acabo la animacion, saca la animacion de la lista if self.frame >= len(anim): self.frame = 0 if len(self.listanim) > 0: self.listanim.pop() self.cerrojo.release()
def regatear(self): self.cerrojo.acquire() if len(self.listanim) <= 0 or (self.listanim[0] != self.regateando0 and self.listanim self.listanim.insert(0, self.regateando0) self.listanim.insert(0, self.regateando1) self.listanim.insert(0, self.regateando0) self.listanim.insert(0, self.regateando1)
21
self.cerrojo.release() def coger(self): self.cerrojo.acquire() if len(self.listanim) <= 0 or self.listanim[0] != self.cogiendo: self.listanim.insert(0, self.cogiendo) self.cerrojo.release() def soltar(self): self.cerrojo.acquire() if len(self.listanim) <= 0 or self.listanim[0] != self.soltando: self.listanim.insert(0, self.cogiendo) self.cerrojo.release() def ocupado(self): return len(self.listanim) def draw(self): """Dibuja el Sprite""" self.cerrojo.acquire() # Si la lista de animaciones es vacia, entonces pon la animacion 0 if self.listanim != []: anim = self.listanim[-1] else: anim = self.esperando # Damos la vuelta a la imagen para que mire al otro lado img = pygame.transform.flip(anim[self.frame][0], self.lado, 0)
# Unimos la imagen a la pantalla, restamos en el eje y para que este en el suelo if self.lado == 0: pos = pygame.Rect(self.rect[0], self.rect[1] - anim[self.frame][1][3], self.rect[ else: pos = pygame.Rect(self.rect[0] - anim[self.frame][1][2], self.rect[1] - anim[self screen.blit(img, pos) self.cerrojo.release()
Hay que tener muy en cuenta algunos trozos de cdigo, en especial, aquellos que devuelvan algo:
# Esta mal:
22
def ocupado(self): self.cerrojo.acquire() # El cerrojo se adquiere return len(self.listanim) # Sale del metodo self.cerrojo.release() # El cerrojo nunca se libera
Este es un problema de estructuracin de cdigo. En general, siempre que se haga uso de variables o mtodos locales a este mtodo (no a la clase), no hace falta proteger el cdigo. Por tanto podemos reescribir el cdigo anterior para que est correcto:
def ocupado(self): self.cerrojo.acquire() # El cerrojo se adquiere tmp = self.listanim # Nueva variable local self.cerrojo.release() # El cerrojo se libera return len(tmp)
Ahora no hay problema de que se calcule la longitud de la lista de animaciones ya que se calcula sobre la copia local. An as, la variable puede contener un valor no actualizado, ya que se puede haber sacado algn elemento de la lista mientras se calculaba la longitud de la lista. Podemos mejorar el cdigo anterior minimizando el nmero de operaciones del return:
# Esta correcto: def ocupado(self): self.cerrojo.acquire() # El cerrojo se adquiere tmp = len(self.listanim) # Nueva variable local self.cerrojo.release() # El cerrojo se libera return tmp # No se opera, solamente se devuelve la variable
Ejercicio propuesto:
Resolver todos los casos conictivos del problema del productor consumidor.
23
El esquema de la aplicacin se divide en los siguientes pasos: 1. Por defecto, el mercader espera hasta que llegue un consumidor (cliente). 2. Mientras, el consumidor intenta alcanzar a un mercader. 3. Una vez hay un mercader y un consumidor, estos entran en una disputa por el precio de la mercanca. 4. Si no hay mercanca y el precio ha sido acordado, ambos esperan. 5. En el momento en que haya una mercanca no tienen que esperar ms. 6. El mercader coge la mercanca. 7. El consumidor recoge la mercanca que el mercader le entrega. 8. El consumidor recoge la mercanca que el mercader le entrega. 9. El consumidor debe ir a la posicin origen donde consumir la mercanca y volver al primer paso. Todo esto se implementar en una hebra, y se llamarn a los mtodos de la clase anterior y de la clase Comprador (regatear, caminar, etc.). De tal forma que podemos ver que la interaccin del programa puede estar en el consumidor, ya que el productor solo pone las mercancas (y no tenemos ningn personaje encargado de colocarlas). As, el consumidor quedara de esta forma:
class Consumidor(threading.Thread):
24
def __init__(self, buff, comp, vend): # Llamada al segundo constructor threading.Thread.__init__(self) self.bb = buff self.comprador = comp self.vendedor = vend # Metodo run def run (self): item = 0 while 1: while self.comprador.posicion()[0] < 340: # Llega a la parte de la derecha self.comprador.caminar(0) while self.comprador.ocupado(): time.sleep(0) while self.comprador.ocupado(): # Esperamos a que llegue time.sleep(0) self.vendedor.regatear() # El tendero regatea self.comprador.regatear() # El comprador regatea con el tendero while self.vendedor.ocupado(): time.sleep(0) while self.comprador.ocupado(): time.sleep(0) self.vendedor.coger() # El tendero coge la mercancia self.vendedor.soltar() # El tendero suelta while self.vendedor.ocupado(): time.sleep(0) self.comprador.coger() # Coge la mercancia while self.comprador.ocupado(): time.sleep(0) item = self.bb.extraer() while self.comprador.posicion()[0] > 40: # Llega al tope izquierdo self.comprador.caminar(1) # Vuelve a la izquierda while self.comprador.ocupado(): time.sleep(0) while self.comprador.ocupado(): # Espera a estar libre para volver time.sleep(0)
Un personaje ocupado no puede atender ninguna otra tarea, por tanto tenemos que tenerlo
25
en cuenta en la planicacin.
Ejercicio propuesto:
Realiza este mismo problema para varios consumidores y varios productores.
Pon un par de sillas y la mesa, adems de los tres elementos que se necesitan para fumar. Puedes ayudarte de la imagen llamada elementos.png, busca en Internet cualquier imagen adicional que quieras aadir. Puedes usar al comprador de la parte anterior como estanquero cuando utilices los Sprites, revisa su hoja de Sprites.
Ejercicio propuesto:
Escribir un programa en Python que implemente el esquema de sincronizacin explicado. Se escribir una clase hebra Estanquero y otra Fumador, especicando en el constructor de esta ltima clase el ingrediente que tiene el fumador. La interaccin entre los fumadores y el estanquero ser resuelta mediante un monitor Estanco. Hacer primero la versin texto y luego ampliarlo mediante Sprites.
26
Como slo existe un carril sobre dicho puente, en un momento dado, slo puede ser cruzado por uno o ms coches en la misma direccin (pero no en direcciones opuestas).
Ejercicio propuesto:
a) Implementar el cdigo del monitor que resuelve el problema del acceso al puente suponiendo que llega un coche del norte (sur) y cruza el puente si no hay otro coche del sur (norte) cruzando el puente en ese momento. b) Mejorar el monitor anterior, de forma que la direccin del traco a travs del puente cambie cada vez que lo hayan cruzado 10 coches en una direccin, mientras 1 ms coches estuviesen esperando cruzar el puente en direccin opuesta. Si se desea animar la resolucin de este problema, se recomienda buscar los elementos grcos de la animacin en pginas web relacionadas con Pygame. La estructura del monitor es la siguiente:
MONITOR GestionaTrafico VAR PROCEDIMIENTO PROCEDIMIENTO PROCEDIMIENTO PROCEDIMIENTO EntrarCocheDelNorte SalirCocheDelNorte EntrarCocheDelSur SalirCocheDelSur
Inicializacion
Ejercicio propuesto:
Escribir un programa usando monitoresd y hebras Python para el problema del barbero durmiente. Los clientes y el barbero son hebras, y la barbera es un monitor que implementa la inter-
27
accin entre stos. Los clientes llaman a cortarPelo para obtener servicio del barbero, despertndolo o esperando a que termine con el cliente anterior. El barbero llama a siguienteCliente para esperar la llegada de un nuevo cliente y servirlo. Cuando termina de pelar al cliente actual llama a finCliente, indicndole que puede salir de la barbera y esperando a que lo haga para pasar al siguiente cliente. Extender el ejercicio para el caso de que existan mltiples barberos en la barbera, cada uno con su propia silla y es posible pelar tantos clientes al mismo tiempo como barberos hay. La estructura del monitor es la siguiente:
MONITOR GestionaTrafico VAR PROCEDIMIENTO cortarPelo PROCEDIMIENTO siguienteCliente PROCEDIMIENTO finCliente Inicializacion