Programación con Hilos en Python
Un Hilo es una unidad básica de utilización de la CPU; comprende un ID de hilo, un contador de programa, un conjunto de registros y una pila. Comparte con otros hilos que pertenecen al mismo proceso la sección de código, la sección de datos y otros recursos del sistema operativo, como los archivos abiertos y las señales. Un proceso tradicional tiene un solo hilo de control. Si un proceso tiene, por lo contrario, múltiples hilos de control, puede realizar más de una tarea a la vez
Los hilos se inventaron para permitir la combinación del paralelismo con la ejecución secuencial y el bloqueo de las llamadas al sistema
Para esto tendremos que importar el paquete threading. Y luego crear una clase que herede de threading.Thread. Si queremos que esta clase tenga un constructor propio tendrá que invocar también al constructor de Thread. Esto lo hace invocando a threading.Thread.__init__(self).
Dentro de la clase rescribiremos el método run. Y dentro de el pondremos el código que queramos que ejecute nuestro hilo.
Luego instanciaremos un objeto con nuestra clase. Y para que empiece a ejecutar solo tendremos que invocar al método start de nuestro objeto.
Un ejemplo simple podría ser el de hilo.py
fijemosnos que todos los hilos comparten la variable d.
A continuación un ejemplo de cómo se ejecutan dos programas secuenciales con hilos.
EL GIL
La ejecución de los threads en Python está controlada por el GIL (Global Interpreter Lock) de forma que sólo un thread puede ejecutarse a la vez, independientemente del número de procesadores con el que cuente la máquina. Esto posibilita que el escribir extensiones en C para Python sea mucho más sencillo, pero tiene la desventaja de limitar mucho el rendimiento, por lo que a pesar de todo, en Python, en ocasiones nos puede interesar más utilizar procesos que threads, que no sufren de esta limitación.
Ejemplo #1
Los hilos se inventaron para permitir la combinación del paralelismo con la ejecución secuencial y el bloqueo de las llamadas al sistema
Para esto tendremos que importar el paquete threading. Y luego crear una clase que herede de threading.Thread. Si queremos que esta clase tenga un constructor propio tendrá que invocar también al constructor de Thread. Esto lo hace invocando a threading.Thread.__init__(self).
Dentro de la clase rescribiremos el método run. Y dentro de el pondremos el código que queramos que ejecute nuestro hilo.
Luego instanciaremos un objeto con nuestra clase. Y para que empiece a ejecutar solo tendremos que invocar al método start de nuestro objeto.
Un ejemplo simple podría ser el de hilo.py
#!/usr/bin/python # Nombre de Fichero : hilo.py import threading from time import sleep class Hilo(threading.Thread): def __init__(self, id): threading.Thread.__init__(self) self.id = id def run(self): sleep(3-self.id) print "Yo soy %s la variable d tiene el valor %s"%(self.id,d) d=1; hilos = [Hilo(1), Hilo(2), Hilo(3)] for h in hilos: h.start()
A continuación un ejemplo de cómo se ejecutan dos programas secuenciales con hilos.
#!/usr/bin/python # Nombre de Fichero : lote.py import threading from os import system from time import sleep class Trabajo(threading.Thread): """ Esta es la clase Trabajo. Los Trabajos se pueden ejecutar de 4 maneras Que esperen a estar solos para ejecutarse. Que esperen a estar solos para ejecutarse y no dejar ejecutar ningun otro hasta terminar. Que no dejen ejecutar a ninguno otro hasta terminar. Que se ejecuten sin restricciones mientras los otros lo dejen """ def __init__(self, id, cmd, entroSolo = False ,salgoSolo = False): threading.Thread.__init__(self) self.cmd = cmd self.id = id self.entroSolo = entroSolo self.salgoSolo = salgoSolo def run(self): system(self.cmd) print "lotePy 0.0.1" tabajos = [Trabajo(1,"calc",True,True), Trabajo(2,"Notepad",True), Trabajo(3,"dir c:\\",True)] for t in tabajos: while (t.entroSolo) and (threading.activeCount() != 1):pass t.start() if (t.salgoSolo): t.join()
EL GIL
La ejecución de los threads en Python está controlada por el GIL (Global Interpreter Lock) de forma que sólo un thread puede ejecutarse a la vez, independientemente del número de procesadores con el que cuente la máquina. Esto posibilita que el escribir extensiones en C para Python sea mucho más sencillo, pero tiene la desventaja de limitar mucho el rendimiento, por lo que a pesar de todo, en Python, en ocasiones nos puede interesar más utilizar procesos que threads, que no sufren de esta limitación.
Ejemplo #1
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- from multiprocessing import Process, Queue
- from urllib2 import urlopen, URLError
- from Queue import Empty
- # Carácteres alfanuméricos.
- NAME_VALID_CHARS = [chr(i) for i in range(48, 58) + range(97, 123)]
- def chars_filter(s):
- """Remover carácteres inválidos."""
- return "".join(
- [c if c in NAME_VALID_CHARS else "" for c in s.lower()]
- )
- def download_page_content(url):
- print "Descargando %s..." % url
- try:
- r = urlopen(url)
- except URLError as e:
- print "Error al acceder a %s." % url
- print e
- else:
- filename = chars_filter(url.lower()) + ".html"
- try:
- f = open(filename, "w")
- except IOError as e:
- print "Error al abrir %s." % filename
- print e
- else:
- f.write(r.read())
- f.close()
- r.close()
- def worker(queue):
- """
- Toma un ítem de la cola y descarga su contenido,
- hasta que la misma se encuentre vacía.
- """
- while True:
- try:
- url = queue.get_nowait()
- except Empty:
- break
- else:
- download_page_content(url)
- def main():
- urls = (
- "http://python.org/",
- "http://perl.org/",
- "http://ruby-lang.org/",
- "http://rust-lang.org/",
- "http://php.net/",
- "http://stackless.com/",
- "http://pypy.org/",
- "http://jython.org/",
- "http://ironpython.net/"
- )
- queue = Queue(9)
- for url in urls:
- queue.put(url)
- processes = []
- for i in range(3):
- processes.append(Process(target=worker, args=(queue,)))
- processes[i].start()
- print "Proceso %d lanzado." % (i + 1)
- for process in processes:
- process.join()
- print u"La ejecución a concluído."
- if __name__ == "__main__":
- main()
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- from multiprocessing import Process, Queue
- from urllib.request import urlopen
- from urllib.error import URLError
- from time import time
- from queue import Empty
- # Carácteres alfanuméricos.
- NAME_VALID_CHARS = [chr(i) for i in list(range(48, 58)) + list(range(97, 123))]
- def chars_filter(s):
- """Remover carácteres inválidos."""
- return "".join(
- [c if c in NAME_VALID_CHARS else "" for c in s.lower()]
- )
- def download_page_content(url):
- print("Downloading %s..." % url)
- try:
- r = urlopen(url)
- except URLError as e:
- print("Error al acceder a %s." % url)
- print(e)
- else:
- filename = chars_filter(url.lower()) + ".html"
- try:
- f = open(filename, "w")
- except IOError as e:
- print("Error al abrir %s." % filename)
- print(e)
- else:
- f.write(r.read())
- f.close()
- r.close()
- def worker(queue):
- """
- Toma un ítem de la cola y descarga su contenido,
- hasta que la misma se encuentre vacía.
- """
- while True:
- try:
- url = queue.get_nowait()
- except Empty:
- break
- else:
- download_page_content(url)
- def main():
- urls = (
- "http://python.org/",
- "http://perl.org/",
- "http://ruby-lang.org/",
- "http://rust-lang.org/",
- "http://php.net/",
- "http://stackless.com/",
- "http://pypy.org/",
- "http://jython.org/",
- "http://ironpython.net/"
- )
- queue = Queue(9)
- for url in urls:
- queue.put(url)
- processes = []
- for i in range(3):
- processes.append(Process(target=worker, args=(queue,)))
- processes[i].start()
- print("Proceso %d lanzado." % (i + 1))
- for process in processes:
- process.join()
- print("La ejecución a concluído.")
- if __name__ == "__main__":
- main()
Ejemplo #4
Comentarios
Publicar un comentario