domingo, 27 de noviembre de 2011

Objeto Semaphore en Python


Uno de los mecanismos más antiguos de sincronización de hilos son los semáforos (Semaphore). Un semáforo permite acceder a un determinado recurso a un número máximo de hilos simultáneamente. Si hay más hilos que el máximo permitido, los pone en espera y los va dejando pasar según van terminando los que están activos. 

Un semáforo actúa como un contador con un valor inicial. 
  • Cada vez que un hilo llama a Semaphore.acquire(), el contador se decrementa en 1 y se deja pasar al hilo. En el momento que el contador se hace cero, NO se deja pasar al siguiente hilo que llame a acquire(), sino que lo deja bloqueado. 
  • Cada vez que se llama a Semaphore.release(), el contador se incrementa en 1. Si se hace igual a cero, libera al siguiente hilo en la cola de espera.
Los semáforos son parecidos a los candados (locks), pero en vez de tomar el valor 1 y 0, toman n valores que nos indicará la cantidad de hilos que puede tomar el recurso concurrentemente.

Por lo tanto, las llamadas que podremos realizar son:
Semaphore ([value])
El argumento opcional proporciona el valor inicial del contador interno. El valor predeterminado es 1.
acquire ([blocking])
Adquirir un semáforo.
Si se invoca sin argumentos: si el contador interno es superior a cero a la entrada, lo decrementa en una unidad y retorna de inmediato. Si es cero a la entrada, bloquear la ejecución del hilo, esperando a que otro llame a release() para hacerlo mayor de cero. Se gestionan de manera adecuada los interbloqueos, por lo que si hay varias llamadas a acquire() bloqueadas a la espera, release() despertará exactamente a una de ellas. La implementación puede seleccionar una al azar, por lo que no se debe confiar en un orden de respuesta observado. No hay valor de retorno en este caso.
Si se invoca con el argumento blocking a verdadero, hacer lo mismo que si se llama sin argumentos y devolver verdadero.
Si se invoca con blocking a falso, no bloquear. Si una llamada sin argumentos bloqueara, devolver falso de inmediato. En caso contrario, hacer lo mismo que si se llama sin argumentos y devolver verdadero.
release ()
Liberar un semáforo, incrementando su contador interno en una unidad. Si era cero a la entrada y otro hilo está esperando a que sea mayor que cero, despertar a dicho hilo.
Los semáforos sirven para permitir el acceso a un recurso que admite un número máximo de hilos simultáneos. Por ejemplo, si cada hilo abre su conexión a base de datos y sólo queremos un máximo de cinco conexiones abiertas simultáneamente, un semáforo puede ser una opción.
Para ver su funcionamiento basta observar el siguiente ejemplo sencillo. En anteriores entradas del blog podemos observar todo lo relativo al módulo Threading. Por otro lado, se debe crear el semáforo indicando el valor inicial del contador (número máximo de hilos que pueden estar activos simultáneamente) :

import threading
from time import sleep
n_sem = 1
semaforo = threading.Semaphore(n_sem)

class Hilo(threading.Thread):
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):
        semaforo.acquire()
        print "Hilo %s entra."%(self.id)
        sleep(3)
        semaforo.release()

hilos = [Hilo(1), Hilo(2), Hilo(3)]

for h in hilos:
    h.start()

Finalmente, Python también nos facilita la clase BoundedSemaphore. La diferencia entre Semaphore y BoundedSemaphore es que, cuando se libera el recurso más veces que el n inicial del semáforo en Semaphore cambia dicha cota, mientras que en BoundedSemaphore lo considera un error de ValueError.

Enlances:
http://pyspanishdoc.sourceforge.net/lib/semaphore-objects.html http://chuwiki.chuidiang.org/index.php?title=Hilos_en_python#Semaphore http://pythonr2.wordpress.com/2008/09/01/sincronizacion-de-hilos-en-python/

Primitivas de sincronización mediante cerrojos: Lock y RLock

Dentro del módulo threading, disponemos de las primitivas de sincronización de hilos mediante cerrojos: Lock y RLock.

Lock

Los cerrojos simples son uno de los mecanismos de sincronización que ofrece el módulo threading. Un cerrojo dispone de los métodos acquire (para bloquear el cerrojo) y release (para desbloquear el cerrojo).

Cuando un hilo llama a acquire por primera vez, bloquea el cerrojo de forma que el próximo hilo que llame a acquire quedará esperando hasta que el cerrojo se desbloquee. Llamando a la función release el cerrojo se desbloqueará y el hilo que esté en espera podrá continuar.

Ejemplo:

lock = threading.Lock()

lock.acquire() # El hilo queda esperando si el cerrojo ha sido bloqueado
# Acceso a recurso compartido
lock.release()


RLock (Re-entrant Lock)

Un cerrojo reentrante funciona de forma similar a un cerrojo simple, pero en este caso un mismo hilo puede llamar a acquire varias veces sin quedar esperando.

Cuando un hilo bloquea un cerrojo, este hilo puede volver a llamar a acquire tantas veces como quiera sin que quede esperando, esto se debe a que esta implementación de cerrojos tiene en cuenta al hilo que ha bloqueado el cerrojo e impide que éste pueda quedar esperando tras un cerrojo que él mismo ha bloqueado.

Si otro hilo distinto al que ha bloqueado el cerrojo intenta llamar a acquire, quedará esperando hasta que el hilo correspondiente lo libere llamando a release.

Para liberar el cerrojo, se deberá llamar a release tantas veces como se haya llamado a acquire.

Ejemplo:

lock = threading.RLock()

lock.acquire()
lock.acquire() # El hilo no quedará esperando

lock.release()
lock.release()



Fuentes:

viernes, 25 de noviembre de 2011

Hilos en Python

Para comenzar, vamos a ver una definición de hilo, que según Wikipedia es la unidad de procesamiento más pequeña que puede ser planificada por un sistema operativo y permite a una aplicación realizar varias tareas a la vez (concurrentemente). Los distintos hilos de ejecución comparten una serie de recursos tales como el espacio de memoria, los archivos abiertos, situación de autenticación, etc. Esta técnica permite simplificar el diseño de una aplicación que debe llevar a cabo distintas funciones simultáneamente.

En el artículo que nos ocupa, vamos a analizar cómo implementar y utilizar los hilos en Python. El módulo que vamos a utilizar es el módulo threading por lo que habrá que impotarlo al comienzo de nuestros programas con la línea:
import threading

sábado, 22 de octubre de 2011

Aprendiendo Python: Cadenas.

Son nativas al lenguaje porque forman parte de él. El tipo para trabajar con cadenas es string, no hay tipo char a diferencia de otros lenguajes.

Python es muy potente en el manejo de cadenas. Veamos algunas características interesantes.

Las cadenas se pueden trocear (slicing). Ejemplo:
>>>cadena = "Martes 13"
>>>print cadena[0:5]
Martes
>>>print cadena[7:]
13

Si son largas, se encierran entre tres comillas. Ejemplo:
>>>cadena = """una cadena más larga que
supercalifragilisticoespialidoso"""

Se pueden utilizar escapes (\n, \t, etc.) así como no interpretarlos. En este último caso las cadenas se denominan cadenas crudas. Ejemplo:
>>>print "\t Hola mundo"
     Hola mundo
>>>print r"\t Hola mundo"
\t Hola mundo

Para comparar cadenas se utilizan los operadores =, <, >, <=, >= y !=. También podemos concatenar y multiplicar cadenas. Ejemplo:
>>>cadena1 = "hola"
>>>cadena2 = " caracola"
>>>print cadena1 + cadena2
hola caracola
>>>print cadena1 * 3
holaholahola

Algunas funciones ya implementadas son:
Conversión a mayúsculas:
>>>cadena = "mayúsculas".upper()
>>>print cadena
MAYÚSCULAS
Conversión a minúsculas:
>>>cadena = "Minúsculas".lower()
>>>print cadena
minúsculas
Máximo de una cadena:
>>>maximo = max("abcde")
>>>print maximo
e
Mínimo de una cadena:
>>>minimo = min("abcde")
>>>print minimo
a
Saber si una parte está contenida en la cadena:
>>>"a" in "juan"
True
Saber si una parte no está contenida en la cadena:
>>>"a" not in "juan"
False

Además podemos importar el módulo string y utilizar todas sus funciones.
>>>import string

Al igual que los números, las cadenas son inmutables. No se pueden cambiar pero se pueden reasignar.

Por último, recordar que cualquier cadena no vacía es True
>>>bool("cadena")
True
y que una cadena vacía es False
>>>bool("")
False     

Aprendiendo Python: Números.

Para manipular números, Python soporta los tipos entero, float y complejo. El tipo float es de doble precisión por lo que sustituye al tipo double de otros lenguajes como C.

Los operadores que podemos utilizar son:
  • Operadores aritméticos: +, -, *, /, ** (para la potencia) y % (para el resto de la división).
  • Operadores de comparación: <, >, <=, >=, ==, !=.
  • Operadores lógicos: and, or, not.

La precedencia de operaciones es la habitual: paréntesis, potencias, multiplicación/división y suma/resta.

Es posible realizar incrementos con los mismos operadores de C (+=, -=, *=, /=). Por ejemplo:
a += 5
a -= 2
Sin embargo, no existen las operaciones incremento y desincremento en uno ++ y --.

En cuanto al tipo bool, cualquier entero distinto a cero es True y el número 0 es False (además de las listas, tuplas, cadenas, etc. que estén vacías).

En Python todo son objetos, por lo que para conocer de qué tipo es un objeto utilizamos la función type(...).

Hay que destacar que en Python los objetos número son inmutables, es decir, no se pueden cambiar. Como curiosidad veremos lo fácil que es hacer un intercambio de objetos (swap) en Python:
a = 1, b = 2, c = 3
a, b = b, a 
En la primera línea asignamos un valor a cada objeto. En la segunda, cambiamos el valor usando tuplas. Ahora a = 2 y b = 1.


martes, 4 de octubre de 2011

Libro de Python y herramientas de estilo

Hola a todos!

He encontrado un libro que nos puede resultar de mucha ayuda acerca de python, ya que es un manual de referencia muy completo, con ejemplos y en español. El autor es Raúl González Duque y el libro está bajo licencia Creative Commons. Aquí os dejo los enlaces:
- Megaupload
- Web oficial

También os dejo una aplicación que resulta de mucha ayuda cuando trabajamos con proyectos en Python. Dado que es un lenguaje interpretado, a veces no sabemos si el código funcionará hasta ejecutar todo el código de nuestro programa, lo cual puede resultar tedioso cuando trabajamos en proyectos medianamente grandes. Se trata de Pylint, una herramienta que comprueba que el código sea correcto en cuanto a sintaxis. Y no solo eso, también comprueba que la escritura del código se ajuste al estándar de Python puntuando tu estilo del 1 al 10 (descrito en la guía de estilo PEP-8). [Página oficial de Pylint]
Los usuarios de Unix podéis instalarlo con el siguiente comando: sudo apt-get install pylint

miércoles, 28 de septiembre de 2011

Clase 28/09/2011

Hoy hemos tenido el primer día de clase.


En ella Rafael nos ha propuesto realizar este blog, junto con otro dedicado a la teoría, para que nos sirva de guía y ayuda para el seguimiento de la asignatura.
También hemos tratado el tema de las licencias Creative Commons. Toda imagen o archivo que subamos, tanto a este como al blog de teoría, deberá poseer licencia Creative Commons. De este modo, además de evitar posibles problemas legales, fomentaremos el uso de los bancos de imágenes con dicha licencia.
Otro tema que hemos tratado es el de herramientas tales como ésta, las RSS, los marcadores sociales como Delicious, o redes sociales, como Twitter o LinkedIn, que pueden sernos de gran ayuda para realizar contactos profesionales, o estar al día en temas de actualidad tecnológica.


A continuación hemos estado viendo una introducción al lenguaje Python. Hemos visto que es un lenguaje interpretado y nociones básicas de cómo programar en dicho lenguaje. Éste lenguaje, en la actualidad, se está desarrollando bajo dos vertientes: la 3.x y la 2.x. Esto se debe a que en las versiones 3.x, se han realizado numerosos cambios, que obligan a reescribir grandes partes de código de las versiones 2.x. Hemos visto que podemos escribir código directamente en la consola de Python, o bien en cualquier editor de texto, como gedit, o en herramientas más especializadas para Python, como idle.


Esto ha sido todo en la clase de hoy. Solo me queda decir que los que queráis ser autores de este blog, mandadme un correo por aquí, o al de la UCO (i62agorr@uco.es) indicando vuestro correo de GMail y os daré de alta.


Saludos!