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
#!/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()
fijemosnos que todos los hilos comparten la variable d.
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
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from multiprocessing import Process, Queue
  4. from urllib2 import urlopen, URLError
  5. from Queue import Empty
  6. # Carácteres alfanuméricos.
  7. NAME_VALID_CHARS = [chr(i) for i in range(48, 58) + range(97, 123)]
  8. def chars_filter(s):
  9. """Remover carácteres inválidos."""
  10. return "".join(
  11. [c if c in NAME_VALID_CHARS else "" for c in s.lower()]
  12. )
  13. def download_page_content(url):
  14. print "Descargando %s..." % url
  15. try:
  16. r = urlopen(url)
  17. except URLError as e:
  18. print "Error al acceder a %s." % url
  19. print e
  20. else:
  21. filename = chars_filter(url.lower()) + ".html"
  22. try:
  23. f = open(filename, "w")
  24. except IOError as e:
  25. print "Error al abrir %s." % filename
  26. print e
  27. else:
  28. f.write(r.read())
  29. f.close()
  30. r.close()
  31. def worker(queue):
  32. """
  33. Toma un ítem de la cola y descarga su contenido,
  34. hasta que la misma se encuentre vacía.
  35. """
  36. while True:
  37. try:
  38. url = queue.get_nowait()
  39. except Empty:
  40. break
  41. else:
  42. download_page_content(url)
  43. def main():
  44. urls = (
  45. "http://python.org/",
  46. "http://perl.org/",
  47. "http://ruby-lang.org/",
  48. "http://rust-lang.org/",
  49. "http://php.net/",
  50. "http://stackless.com/",
  51. "http://pypy.org/",
  52. "http://jython.org/",
  53. "http://ironpython.net/"
  54. )
  55. queue = Queue(9)
  56. for url in urls:
  57. queue.put(url)
  58. processes = []
  59. for i in range(3):
  60. processes.append(Process(target=worker, args=(queue,)))
  61. processes[i].start()
  62. print "Proceso %d lanzado." % (i + 1)
  63. for process in processes:
  64. process.join()
  65. print u"La ejecución a concluído."
  66. if __name__ == "__main__":
  67. main()
Ejemplo #2

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from multiprocessing import Process, Queue
  4. from urllib.request import urlopen
  5. from urllib.error import URLError
  6. from time import time
  7. from queue import Empty
  8. # Carácteres alfanuméricos.
  9. NAME_VALID_CHARS = [chr(i) for i in list(range(48, 58)) + list(range(97, 123))]
  10. def chars_filter(s):
  11. """Remover carácteres inválidos."""
  12. return "".join(
  13. [c if c in NAME_VALID_CHARS else "" for c in s.lower()]
  14. )
  15. def download_page_content(url):
  16. print("Downloading %s..." % url)
  17. try:
  18. r = urlopen(url)
  19. except URLError as e:
  20. print("Error al acceder a %s." % url)
  21. print(e)
  22. else:
  23. filename = chars_filter(url.lower()) + ".html"
  24. try:
  25. f = open(filename, "w")
  26. except IOError as e:
  27. print("Error al abrir %s." % filename)
  28. print(e)
  29. else:
  30. f.write(r.read())
  31. f.close()
  32. r.close()
  33. def worker(queue):
  34. """
  35. Toma un ítem de la cola y descarga su contenido,
  36. hasta que la misma se encuentre vacía.
  37. """
  38. while True:
  39. try:
  40. url = queue.get_nowait()
  41. except Empty:
  42. break
  43. else:
  44. download_page_content(url)
  45. def main():
  46. urls = (
  47. "http://python.org/",
  48. "http://perl.org/",
  49. "http://ruby-lang.org/",
  50. "http://rust-lang.org/",
  51. "http://php.net/",
  52. "http://stackless.com/",
  53. "http://pypy.org/",
  54. "http://jython.org/",
  55. "http://ironpython.net/"
  56. )
  57. queue = Queue(9)
  58. for url in urls:
  59. queue.put(url)
  60. processes = []
  61. for i in range(3):
  62. processes.append(Process(target=worker, args=(queue,)))
  63. processes[i].start()
  64. print("Proceso %d lanzado." % (i + 1))
  65. for process in processes:
  66. process.join()
  67. print("La ejecución a concluído.")
  68. if __name__ == "__main__":
  69. main()
Ejemplo #3



Ejemplo #4








Comentarios

Entradas populares