Concu Python

18
Programación concurrente en Python2.4 Alumnos: Pablo Nebreda Cespedosa Yaiza Temprado Rodríguez Grupo < 02 > Programación Concurrente y Distribuida 5º Ingeniería en Informática Escuela Superior Politécnica Universidad Europea de Madrid Curso 2005-2006 Profesor : José Carlos Cortizo Pérez Departamento de Sistemas Informáticos

Transcript of Concu Python

Page 1: Concu Python

Programación concurrente en Python2.4

Alumnos:Pablo Nebreda Cespedosa

Yaiza Temprado Rodríguez…

Grupo < 02 >

Programación Concurrente y Distribuida5º Ingeniería en InformáticaEscuela Superior Politécnica

Universidad Europea de MadridCurso 2005-2006

Profesor : José Carlos Cortizo Pérez

Departamento de Sistemas Informáticos

Page 2: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

ÍNDICE

1. INTRODUCCIÓN A PYTHON.......................................................................3

1.1. Características del lenguaje............................................................................3

1.2. Tipos de datos................................................................................................5

2. CONCURRENCIA EN PYTHON....................................................................7

2.1 El módulo Thread...........................................................................................7

2.2 Locks...............................................................................................................8

2.3 El Global Interpreter Lock..............................................................................9

2.4 El módulo Threading .....................................................................................92.4.1 El objeto Thread.........................................................................................102.4.2 Conditions..................................................................................................11

3. RESOLUCIÓN DE PROBLEMAS CONCURRENTES...............................12

3.2 El problema del productor consumidor.........................................................13

3.3 El problema de los lectores escritores...........................................................15

4. Bibliografía.....................................................................................................18

-2-

Page 3: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

1. Introducción a Python.

1.1. Características del lenguaje.

Python es un lenguaje de programación fácil de aprender y potente. Tiene eficacesestructuras de datos de alto nivel y una solución de programación orientada a objetossimple pero eficaz. La elegante sintaxis de Python, su gestión de tipos dinámica y sunaturaleza interpretada hacen de él el lenguaje ideal para guiones (scripts) y desarrollorápido de aplicaciones, en muchas áreas y en la mayoría de las plataformas.

Además, la característica de ser orientado a objetos le proporciona la capacidad de serusado para la programación de aplicaciones reales, a diferencia de otros lenguajes descript como Perl o Bash.

Aunque no nos extenderemos demasiado con la introducción a la sintaxis, pues no es elobjetivo de este documento, sí que repasaremos algunas de las características másllamativas de Python. No obstante, si el lector desea aprender más sobre este interesantelenguaje, puede encontrar una gran cantidad de información gratuita en la red1.

Alguna de sus características son:

• Asignación dinámica de tipos:En Python no es necesario definir las variables antes de que éstas seanreferenciadas y utilizadas. En el momento en el que se usa la variable por primeravez, ésta se crea, asignándosele dinámicamente el tipo correspondiente al valorque se le esté asignando. Por ejemplo:

aux1 = “hola” # acabamos de crear una variable stringaux2 = 3.5 # acabamos de crear una variable float

• Tipos básicos y complejos:Además de los tipos básicos inherentes a cualquier lenguaje de programaciónactual (enteros, float, strings), existen otros más complejos, como las listas losdiccionarios o las tuplas. Más adelante veremos exactamente en qué consiste cadauno de ellos.

• Clases e instancias, módulos y paquetes:Como hemos dicho, Python es un lenguaje orientado a objetos, por lo que cuentacon la estructuración en clases propia de este paradigma. Además, éstas puedenagruparse después de módulos, y estos a su vez, en paquetes; creando estructurascada vez más abstractas.

1Dive into Python: http://diveintopython.org/

-3-

Page 4: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

• Funciones:En Python todo, absolutamente todo, es una función. Todas las clases, lasvariables y paquetes son funciones. Con esto estamos asegurando que cualquierestructura devuelve un valor y puede recibir una serie de parámetros.

• Excepciones:Como todo lenguaje que se precie, Python también cuenta con un juego deexcepciones predefinidas. Aunque proporciona la posibilidad de que el usuario sedefina sus propias excepciones. Por supuesto, las excepciones también sonfunciones.

• Fuertemente tipado:En Internet existe cierta controversia sobre el significado que tiene esta palabra alreferirse a los lenguajes de programación, por lo que podremos leer tanto quePython no es tipado en absoluto, como que es fuertemente tipado. Según nuestrapropia opinión y la opinión más generalizada, Python es fuertemente tipado en elsentido de que no permite ningún tipo de conversión automática (a diferencia deotros lenguajes como C). Ejemplo:

a = 3 b = “a”c = a + b #error, necesita conversión explícita

• Asignación por referencia:Como su propio nombre indica, cuando creamos una variable compleja (dícese delistas, diccionarios o tuplas) y asignamos su valor a otra variable, si modificamosuna de ellas la otra sufrirá el mismo cambio; ya que ambas apuntan a la mismadirección de memoria. Ejemplo:

a = [1, 2, 3]b = aa.append(4)print (b) #mostrará por pantalla [1,2,3,4]

• Necesidad de indentación:En Python no existe ningún delimitador de las estructuras de control. Es decir, noexiste ningún símbolo (como pueden ser las llaves de otros lenguajes) que indiqueque una función, un bucle o una clase ha terminado. Por ello, la indentación enPythton es obligatoria, ya que es la única manera de que el intérprete sepa hastadónde llega cada estructura.

El resultado de esta obligación es que, aunque al principio pueda resultar incómodo, termina convirtiéndose en un hábito, dando lugar a un código mucho más cómodo de leer. Ejemplo:

-4-

Page 5: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

for i in range(20):if i%3 == 0:print iif i%5 == 0:print "Bingo!"print "---"

• Shell interactivo:El shell interactivo nos permite realizar pruebas sobre funciones particularesrápidamente, sin necesidad de ejecutar un programa completo (como ocurre conPHP), ni mucho menos compilarlo (como ocurre con C). Simplemente, abrimos elshell y escribimos órdenes en Python como si estuviéramos introduciendocomandos. Ejemplo:

>>> print "Hello, world"

Hello, world

>>> x = 12**2

>>> x/2

72

>>> # this is a comment

1.2. Tipos de datos

1.2.1.Tipos básicos:

• Numerales:En el caso de los tipos básicos numerales, éstos no se diferencian mucho de los decualquier otro lenguaje, aunque proporcionan muchas funciones para haceroperaciones matemáticas rápidamente. De hecho, esta característica junto con elde la consola interactiva, han provocado que algunas personas usen Python comocalculadora. Algunos ejemplos:

12, 3.14, 0xFF, 0377, (-1+2)*3/4**5, abs(x), 0<x<=5

• Strings:Además del tipo básico de string, proporciona numerosas funciones para elmanejo de las mismas. Lo que convierte a Python en una excelente elección a lahora de desarrollar parseadores. Ejemplos:

“hola"+“mundo" # concatenación -> “holamundo"“hola"[1:4] # slicing -> “ola"

len(“hola") # longitud -> 4

-5-

Page 6: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

1.2.2. Tipos complejos:

• Listas:Las listas son colecciones de datos heterogéneos, de forma que son almacenadossecuencialmente. A continuación se muestra un ejemplo de lista en la que seintroducen tipos de datos distintos (sin necesidad de funciones de conversión) y semuestran algunas de las funciones que pueden usarse sobre ellas.

a = [99, "bottles of beer", ["on", "the", "wall"]]>>> a = range(5) # [0,1,2,3,4]>>> a.append(5) # [0,1,2,3,4,5]>>> a.pop() # [0,1,2,3,4], y devuelve '5'

• Tuplas:Similares a las listas, con la única diferencia que los valores de los elementos quecontienen no pueden ser modificados una vez asignados. Ejemplo:

>>> tupla = ('hola', 'bienvenido', 'a', 'python')>>> tupla[1] = 'intentamos cambiar'

TypeError: object doesn't support item assignment

• Diccionarios:Este tipo de datos es equivalente a las funciones hash de Java. Es decir, undiccionario está formado por elementos, que a su vez están formados por dosvalores: el elemento propiamente dicho y una clave que lo identificaunívocamente. De esta manera, podemos acceder a cualquiera de los elementosdirectamente a través de su clave. Elemplo:

>>> dic = {1:'hola', 2:'bienvenido', 'clave':'a', -2:'python'}>>> dic.keys()[1, 2, 'clave', -2]

-6-

Page 7: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

2. Concurrencia en Python.

Python es un lenguaje con soporte multihilo, lo que nos permite la ejecución de variosprocesos simultáneamente. Esta funcionalidad, básica en cualquier sistema operativoactual, nos proporciona la posibilidad de crear programas con una mayor capacidad derespuesta.

En el caso de Python, los hilos están implementados y se pueden manejar a través delos módulos thread y threading, ambos referenciados en el API oficial2. En realidad,thread no es más que una versión a más bajo nivel que threading, por lo que en laprogramación habitual se suele usar esta última, por ser más cómoda para elprogramador.

La implementación de hilos, procesos y sincronización en Python, siguiendo lafilosofía del lenguaje, es en principio una tarea sencilla. Sin embargo, el añadir hilos a unprograma en Python para hacerlo más eficiente no siempre es el paso correcto. Esto esdebido al Global Interpreter Lock (GIL), en castellano, Interprete Global de Bloqueos que

protege las estructuras internas de datos de Python. Veremos más en profundidad esteconcepto más adelante.

2.1 El módulo Thread

Provee primitivas de bajo nivel para trabajar con múltiples hilos compartiendo elmismo espacio global de datos.

Un primer ejemplo:

import timeimport thread

def myfunction(string,sleeptime,*args): while 1: print string time.sleep(sleeptime)

if __name__=="__main__":

thread.start_new_thread(myfunction,("Thread No:1",2)) while 1:pass

Empezamos un Nuevo thread llamando a start_new_thread() que toma la dirección delobjeto junto con los argumentos que se le pasan en la tupla.

2 Threads: http://www.python.org/doc/current/api/threads.html Threading: http://www.python.org/doc/lib/module-threading.html

-7-

Page 8: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

2.2 LocksPara hacer correr varios threads simplemente se llama a la función start_new_thread()

varias veces. Ahora el problema es la sincronización, que se soluciona a través de losobjetos Lock.

Se usan para manejar las diferentes secciones críticas del código. Un hilo entra en lasección crítica llamando a acquire(), y sale de ella llamando a release(). Pensemos en elobjeto Lock como en el testigo de una carrera de relevos. Sólo uno de los corredorespuede tener el testigo en cada momento; y sólo cuando lo suelta podrá ser cogido por otrode los inidividuos.

Veamos un ejemplo:

#!/usr/bin/env python

import timeimport thread

def myfunction(string,sleeptime,lock,*args): while 1:

# obtenemos permiso para acceder a la SC lock.acquire()

# tras adquirir el Lock, suspendemos el hilo time.sleep(sleeptime)

# aqui vendria la SC

# soltamos el Lock para que entre otro hilo lock.release()

time.sleep(sleeptime)

if __name__=="__main__":

lock=thread.allocate_lock() thread.start_new_thread(myfunction,("Thread No:1",2,lock)) thread.start_new_thread(myfunction,("Thread No:2",2,lock))

while 1:pass

Vemos que llamamos a lock.aquire() justo antes de la S.C. y a lock.release() justodespués de salir de ella. Si bien hay un pequeño matiz en este código: el uso de la funcióntime.sleep().

Esta es usada para brindar a todos los hilos la oportunidad de entrar en la seccióncrítica. El problema es que el intérprete de Python no asegura directamente que todos loshilos tengas las mismas posibilidades de entrar en la sección crítica, ya que noimplementa directamente multihilo.

-8-

Page 9: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

A diferencia de otros lenguajes como Java, donde los hilos forman parte de la sintaxis,en Python constituyen un módulo opcional, lo que proporciona cierto tipo de problemas.Tal y como expresan en la documentación:

• No todas las funciones que se bloquean en espera de alguna operación de entraday salida permiten seguir corirendo a otros threads.

• No es posible interrumpir una llamada a acquire() en un bloqueo.

Esto quiere decir que un código como el siguiente:

while 1:lock.acquire().....#some operation.....lock.release()

seguramente causará inanición a uno o más hilos.

2.3 El Global Interpreter Lock

Actualmente el intérprete de Python no es thread safe. No hay prioridades ni grupos, ylos hilos no pueden ser suspendidos ni parados. El soporte que proporciona es muy básicoy no implementa este tipo de funcionalidad. No obstante, con el módulo threading sepuede obtener un soporte para la concurrencia que cubre esta carencia.

En realidad sólo hay un hilo corriendo a cada instante, por lo que para soportarprogramas multihilo hay un bloqueo global que debe ser retenido por el hilo actual antesde acceder a los objetos de Python de manera segura.

El intérprete, para asegurar la ejecución multihilo, periódicamente adquiere y libera elLock, por defecto cada 10 instrucciones del bytecode (aunque este tiempo se puedecambiar usando la función sys.setcheckinterval()). También es liberado y readquiridocuando se realiza una operación de I/O como escribir o leer de fichero; de esta maneraotros hilos puede seguir ejecutándose mientras se realiza la operación de I/O.

El intérprete de Python almacena información de cada hilo en una estructura llamadaPyThreadState.

2.4 El módulo Threading

El modulo Threading usa el paquete thread para proveer al programador de interfacesde alto nivel. Contiene mecanismos para prevenir el interbloqueo de las seccionescríticas, y por supuesto, también contiene métodos wait y notify, semáforos, etc.

-9-

Page 10: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

Los componentes más usado del módulo son:

• Objeto Lock:Es una primitiva de sincronización que no pertenece a ningún hilo en particular.En Python, esta es la primitiva de sincronización de más bajo nivel que haydisponible, y se encuentra implementada directamente con el módulo thread.Sólo cuenta con las funciones acquire() y release().

• Objeto Rlock:Un Lock reentrante es una primitiva de sincronización que puede ser adquirida (através de la función acquire()) múltiples veces por el mismo hilo. Esta primitivausa el concepto de recursividad unido a las posiciones lock y unlock originales; demanera que que el cerrojo sólo es liberado cuando se haya liberado tantas vecescomo se adquirió. Igualmente, cuenta con las funciones acquire() y release().

• Objeto Semaphore:Es una de las primitivas de sincronización más antiguas. El semáforo maneja uncontador interno que se decrementa con cada llamada a acquire() y se incrementacon cada llamada a release(). El contador nunca puede bajar de cero. Cuando unacquire() se encuentra con que el valor del semáforo es cero, se bloquea esperandoa que otro hilo ejecute la función release(). Las únicas funciones que implementason esas dos, acquire() y release().

• Objeto Event:Este es uno de los mecanismos más sencillos para la comunicación entre hilos. Unhilo ejecuta un evento, mientras que otro espera a que se ejecute. Además, cuentacon un flag interno que indica si existe un evento o no.

• Objeto Thread:Del objeto Thread, por ser la parte central de toda la concurrencia en Python,hablaremos más en profundidad a continuación.

• Objeto Condition:Lo veremos en mayor profundidad un poco más adelante.

2.4.1 El objeto Thread

El objeto Thread nunca se usa directamente, sino a través del interfaz threading.Thread,sobrescribiendo el método __init__() o la función run(). Tenga en cuenta que en el casode llamar sobrescribir el método __init__() deberá llamar a Thread.__init()

Veamos un ejemplo:

-10-

Page 11: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

import timefrom threading import Thread

class MiHilo(Thread):

def __init__(self,num):

Thread.__init__(self) self.bignum=bignum def run(self):

for l in range(10): for k in range(self.num): res=0 for i in range(self.num): res+=1

def test(): num=1000 thr1=MyHilo(num) thr1.start() thr1.join() if __name__=="__main__": test()

El hilo no empezará ha ejecutarse hasta que se llame al método start().El método join() hará esperar al hilo que lo invoca hasta que termine la ejecución.

2.4.2 Conditions.

Usar objetos Conditions es una manera de asegurarnos el acceso sincronizado entremúltiples hilos que esperar a que una determinada condición sea cierta para empezaralgún procesamiento. Son una manera muy elegante de implementar el problema delProductor Consumidor.

Una variable Condition siempre va asociada a algún tipo de Lock, por lo que sino existe, crea su propio objeto RLock. La variable de condición cuenta con losmétodos acquire() y release(), que llaman a los correspondientes métodos delLock asociado. Además, cuenta con los métodos wait(), notify() y notifyAll(), conlas siguientes utilidades:

wait(): libera el Lock y bloquea al hilo hasta que es despertado. notify(): despierta uno de los hilos que se encuentran esperando. notifyAll(): despierta a todos los hilos que se encuentran

esperando.

-11-

Page 12: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

3. Resolución de problemas concurrentes.

3.1 El problema de la panadería.

3.2.1 El algoritmo de la panadería.

En el algoritmo de la panadería al proceso que va a entrar en su sección crítica se le daun número mayor que los dados anteriormente, y espera a que su ticket tenga el valor másbajo. Tal y como ocurre en las panaderías o tiendas similares (mercados, pescaderías,etc.), donde se recoge un ticket y se espera el turno. La sección crítica en esta analogíasería cuando el cliente habla con el panadero, que sólo puede atender a una persona a lavez, hasta que termina de cobrar.

Con este algoritmo, al contrario que el algoritmo de Dekker que es su equivalente parados procesos, no es necesaria una variable a la que acceden los procesos tanto para lecturacomo para escritura. Se tienen dos arrays, Choosing y Number; Choosing se usa comoflag para indicar al resto de procesos que está escogiendo un número ( 1 == escogiendo).

Un proceso puede entrar en su sección crítica cuando en su posición del array Numbertiene el número más bajo; en caso de empate se escoge el proceso con índice en el arraymás bajo. Cuando termina su sección crítica pierde su número y sólo puede volver aentrar a ella cogiendo nuevamente otro y esperando su turno.

3.2.2 Problema resoluble no descomponible en N tareas idénticas

Ahora tenemos N procesos que ejecutan diferentes tareas en la sección crítica;conceptualmente: uno compra pan, otro pide pan, harina y leche, otro tarda más en pagarque el resto, etc.). A nivel de implementación, tenemos procesos cuya operación es sumar1, restar 1 o elevar al cuadrado el valor del recurso.

• Bucle principal:

hilos = []op = 1for i in range(4):

if i % 3 == 0:op = 1

elif (i % 3) == 1:op = 2

else:op = 3

hilos.append(HiloCliente(i, op))hilos[i].start()time.sleep(5.0)for i in range(4):

-12-

Page 13: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

hilos[i].join()

• Fragmento de traza resultante de la ejecución del script con N = 4,y un intervalo temporal T = 5:

SC P3Sumo uno, valor del recurso compartido: 4PostpP3

SC P0Sumo uno, valor del recurso compartido: 5PostpP0

SC P1Elevo al cuadrado, valor del recurso compartido: 25PostpP1

SC P2Restar uno, valor del recurso compartido: 24PostpP2

Cada proceso accede a la sección crítica sólo cuando está libre, realizando la operaciónque tenga asignada (incremento, decremento o el cuadrado del número).

3.2 El problema del productor consumidor.

3.2.1 El problema del productor – consumidor.

El problema del productor – consumidor es una abstracción para los problemas decomunicación, al igual que el problema de la exclusión mutua tratado en el apartadoanterior es una abstracción para problemas de sincronización.

Los dos tipos de procesos existentes en el problema son procesos de tipo producto y detipo consumidor. De modo que el planteamiento general es el siguiente: uno o másproductores generan cierto tipo de datos (registros, caracteres, etc.) y los sitúan en unbuffer. A su vez, uno o varios consumidores sacan elementos del buffer de uno en uno.

La solución del problema tiene que ser tal que impida la superposición de estasoperaciones sobre el buffer. Es decir, sólo un proceso (productor o consumidor) puedeacceder al buffer en un instante dado.

De esta manera desacoplamos ambos procesos (productor y consumidor) haciendo quecada uno trabaje a su ritmo; aunque si existe gran disparidad de velocidades lo único quehacemos en realidad es una solución que presentará el mismo efecto de ir a remolque del

-13-

Page 14: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

más lento, que es el defecto que presenta la solución secuencial.

3.2.2 Solución mediante semáforos.

Los semáforos nos proporcionan los mecanismos necesarios para ofrecer una soluciónelegante al problema del productor – consumidor, ya que nos permite asegurar desde unnivel de abstracción relativamente alto que las operaciones, tanto de inserción como deeliminación, no pueden producirse simultáneamente.

La estructura de un semáforo es inicializado con un valor numérico no negativo que irádisminuyendo o aumentando a lo largo de su ejecución, y tiene dos operaciones básicas:

• Wait: si el número interno del semáforo es superior a cero, lo decrementa en unaunidad. Si es 0, el proceso que haya llamado a la instrucción wait del semáforoqueda bloqueado.

• Signal: si existe algún proceso bloqueado, lo despierta. En caso contrario, aumenta en una unidad la cuenta interna del semáforo.

Otra de las características de los semáforos es que se nos garantiza que las operacioneswait y signal son atómicas, por lo que no deben preocuparnos posibles interfoliaciones noválidas entre instrucciones más simples.

La atomicidad, unida al número interno del semáforo, nos sugieren una solucióninteresante: dos semáforos, “elementos” y “espacios”, inicializado “elementos” a 0(comenzamos con un buffer vacío), y espacios inicializado al tamaño del buffer(empezamos con el máximo número de espacios posibles), de manera que cuando unproductor genera un elemento, se aumenta en uno el contador de “elementos” y sedecrementa en uno el valor de “espacios”. Cuando se consume un elemento, sedecrementa en uno el valor de “elementos” y se aumenta en uno el valor de “espacios”.

La idea, por tanto, es sencilla: un productor sólo se bloqueará cuando el buffer estélleno (contador de “espacios” igual a 0), y un consumidor sólo se bloqueará cuando elbuffer esté vacío (contador “elementos” igual a 0).

3.2.3 Solución del productor – consumidor sobre un buffer circular

En el modelado inicial de nuestro problema tenemos un proceso Productor y otroConsumidor, de manera que irán introduciendo y sacando los elementos de un arraycircular que actuará como buffer. La sección crítica (la inserción o eliminación), comopuede verse en el código, queda protegida a través de un wait tanto en el Productor como

en el Consumidor justo antes de su ejecución.

-14-

Page 15: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

• Bucle principal de la clase Productor:

while 1 :#wait()espacios.acquire()#seccion criticaprint currentThread(), "Se ha producido un elemento"buffer.producir()#signal()elementos.release()time.sleep(self.tiempoespera)

• Bucle principal de la clase Consumidor:

while 1:#seccion no criticatime.sleep(self.tiempoespera)#wait()elementos.acquire()#seccion criticabuffer.consumir()#signal()espacios.release()print currentThread(), "Se ha consumido un elemento"

• Bucle principal de la ejecución:

b = Buffer()elementos = Semaphore(0)espacios = Semaphore(b.tamanyo + 1)pro = Productor(elementos, espacios, b)cons = Consumidor(elementos, espacios, b)pro.start()cons.start()

Como puede verse, se inicializan los semáforos al número de elementos y espaciosiniciales respectivamente, se crean el productor y el consumidor y se lanza su ejecución.

Vemos que en el bucle del Productor, antes de entrar en la SC nos aseguramos de quequedan espacios (a través de espacios.acquire), para al salir de la sección crítica despertara algún proceso o aumentar en uno la cuenta de elementos en el buffer.

De la misma manera, en el caso del Consumidor nos aseguramos antes de entrar en laSC de que hay elementos en el buffer, y a la salida despertamos un proceso oaumentamos en uno la cuenta de espacios vacíos.

3.3 El problema de los lectores escritores.

3.3.1 El problema de los lectores escritores

-15-

Page 16: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

El problema de los lectores escritores es similar al problema de la exclusión mutua enel sentido de que varios procesos compiten por el acceso a la sección crítica. En este caso

dividimos el problema en dos clases:

• Lectores: que no requieren de exclusión entre sí• Escritores: que necesitan de exclusión tanto entre sí como con los lectores.

El problema es una abstracción del acceso a las bases de datos, donde no hay problemaen tener varios procesos leyendo concurrentemente pero si escribiendo (modificando) elcontenido. El proceso que quiere leer llama al monitor empezar_leer y el que quiereescribir llama a empezar_escribir. De igual modo al terminar llaman a los procesosterminar_leer y terminar_escribir respectivamente. Dicho esto debe quedar claro que losprocedimientos de inicio causarán la suspensión de los procesos mientras que losprocedimientos de terminación causarán el signal.

El monitor contiene dos variables de estado:

• Readers: que cuenta el número de lectores que han pasado satisfactoriamenteempezar_leer y actualmente están leyendo.empezar_leer y actualmente están leyendo.

• Writing: un flag, marcado a verdadero cuando hay un proceso escribiendo.Se suspende un lector si hay un proceso escribiendo o esperando a escribir. Y elescritor es suspendido solo si hay procesos leyendo o escribiendo.

• Bucle principal de la clase Lector:

while 1:if ((papel.writing == 1) | (ok_to_write.vacia == 0)):

ok_to_read.condicion.acquire()papel.empezar_leer()

ok_to_read.condicion.release()print currentThread(), "Leer el dato"papel.terminar_leer()if papel.readers == 0:

ok_to_write.condicion.acquire()time.sleep(self.sleeptime)

• Bucle principal de la clase Escritor:

while 1:adquirido = 0if ((papel.readers != 0) | (papel.writing == 1)):

ok_to_write.condicion.acquire()adquirido = 1papel.empezar_escribir()print currentThread(), "Escribir un dato"papel.terminar_escribir()

if (ok_to_read.vacia == 0):ok_to_read.condicion.release()

-16-

Page 17: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

else:if (adquirido == 1):ok_to_write.condicion.release()

time.sleep(self.sleeptime))

• Bucle principal de la ejecución:

q = papel()ok_to_read = Aux()ok_to_write = Aux()writer1 = Escritor(ok_to_write,ok_to_read,q)reader1 = Lector(ok_to_write,ok_to_read,q)reader2 = Lector(ok_to_write,ok_to_read,q)#reader3 = Lector(ok_to_write,ok_to_read,q)writer1.start()reader1.start()reader2.start()while 1: pass

El uso de la clase auxiliar es debido a que en Python la clase Condition no traeimplementado un método que indique que la cola de la condición está o no vacía, es porello que creamos una clase que contenga a Condition con una variable booleana queindique su estado.

-17-

Page 18: Concu Python

Concurrencia en Python Programación Concurrente y Distribuida

______________________________________________________________________________________

4. Bibliografía

http://www.python.org/doc/lib/contents.html Sitio oficial – Guía de referencia

http://mindview.net/Books/TIPython Thinking in Python (libro descargable)

http://diveintopython.org/ Dive into Python (libro descargable)

http://linuxgazette.net/107/pai.html Understanding Threading in Python

-18-