¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Python 3
  3. Programación en paralelo
Extrait - Python 3 Tratamiento de los datos y técnicas de programación
Extractos del libro
Python 3 Tratamiento de los datos y técnicas de programación
1 opinión
Volver a la página de compra del libro

Programación en paralelo

Utilización de un hilo de ejecución

1. Gestión de un hilo de ejecución

a. Presentación

Hace tiempo que Python propuso un módulo llamado thread, que ofrecía un API de bajo nivel y que permitía crear hilos de ejecución, según diferentes soluciones que podrían corresponder con las capacidades de Python en este dominio.

La implementación CPython, teniendo partes completas de su código incompatibles con una creación propia de hilos de ejecución, este módulo ha evolucionado y finalmente ha quedado obsoleto con la finalización de un API de alto nivel, threading, que no solo es más sencillo de utilizar y está más próximo a las necesidades, sino que también más fiable.

Para Python 3.x, el antiguo y obsoleto módulo de bajo nivel, se ha eliminado (renombrado con un subrayado (http://www.python.org/dev/peps/pep-3108/), ver la PEP en el capítulo Obsoleto) y el nuevo módulo se beneficia de todos los esfuerzos añadidos a la implementación de CPython, para permitir tener varios hilos de ejecución.

Como veremos, la problemática no es la creación y ejecución en paralelo de hilos de ejecución, sino más bien la gestión de los recursos que comparten y sus comunicaciones.

b. Creación

Para crear un hilo de ejecución, hay que utilizar el módulo de alto nivel:

>>> from threading import Thread 

Para las necesidades de este ejemplo, también cargamos esto:

>>> from time import time, ctime, sleep 

A continuación se muestra la declaración de un hilo de ejecución, siguiendo su uso:

>>> class Worker(Thread):  
...     def __init__(self, name, delay):  
...         self.delay = delay  
...         Thread.__init__(self, name=name)  
...     def run(self):  
...         for i in range(5):  
...         print("%s: llamada %s, %s" % (self.getName(), i, 
ctime(time())))  
...              sleep(self.delay)  
...   
>>> try:  
...     t1 = Worker("T1", 2)  
...     t2 = Worker("T2"...

Utilización de proceso

1. Gestión de un proceso

a. Presentación

En un determinado número de planes, visto desde la interfaz de alto nivel proporcionada por Python, la multitarea (o multi-hilo), se parece al multiproceso. Pero la semejanza termina en las listas de métodos propuestos por los objetos.

La problemática a resolver es más compleja. Su ventaja es permitir ejecutar dos acciones al mismo tiempo sobre dos procesadores diferentes y por lo tanto, reducir su tiempo de ejecución.

Un proceso es independiente, incorpora su propio entorno de ejecución. También está relacionado con el proceso que lo crea, que puede ser la consola Python, la máquina virtual Python u otro proceso Python.

b. Creación

Crear un proceso es relativamente sencillo. A continuación se muestran los módulos necesarios:

>>> from multiprocessing import Process  
>>> from time import sleep 

A continuación se muestra un trabajo a adjuntar al proceso:

>>> def work(name):  
...     print('Inicio del trabajo: %s' % name)  
...     for j in range(10):  
...         for i in range(10):  
...             sleep(0.01)  
...             print('.', sep='', end='')  
...         print('.')  
...     print('Fin del trabajo: %s' % name)  
... 

Ahora es suficiente con crear el proceso y arrancarlo:

>>> p = Process(target=work, args=('Test',))  
>>> p.start()  
>>> p.join()  

A continuación se muestra el resultado:

Inicio del trabajo: Prueba  
...........  
...........  
...........  
...........  
...........  
...........  
...........  
...........  
...........  
...........  
Fin del trabajo: Test 

Vamos a destacar las características del proceso:

>>> print ('Flujo principal: %s' % os.getpid())  
Flujo principal: 8879 

El PID padre del proceso es este número:

>>> def work(name):  
...     print('Inicio del trabajo: %s' % name) 
...  ...

Ejecución asíncrona

1. Introducción

Hasta ahora, se ha visto cómo crear hilos de ejecución y gestionarlos con herramientas de alto nivel, que permiten asignar el trabajo a cada hilo de ejecución, cómo utilizarlos lo mejor posible y gestionar su ejecución de manera adecuada.

También hemos visto cómo crear procesos y gestionarlos siempre con las herramientas de alto nivel.

Estas operaciones, respecto a lo que implican como técnica para los lenguajes de bajo nivel, son relativamente simples de utilizar y hemos visto diferentes casos de aplicación y diferentes implementaciones posibles.

También es posible realizar operaciones de más bajo nivel todavía, como el uso del fork, que sigue estando disponible, incluso si este capítulo no insiste especialmente en esta funcionalidad, prefiriendo poner el foco en el alto nivel.

De esta manera, los que conocen el fork en C, serán capaces de efectuar la misma operación en Python, porque se trata de lo mismo. Además, las reglas de programación son comunes a todos los lenguajes.

Sin embargo, el uso del bajo nivel, incluso de los dos módulos de alto nivel que son threading y multiprocessing, añade riesgos y hace necesario cierto dominio, incluso si esto es mucho más sencillo que con los lenguajes de más bajo nivel, como el C.

El gran riesgo es crear un proceso o un hilo de ejecución sobre el que perdamos el control. Si sucede algo así, el hilo de ejecución o el proceso puede llegar a ocupar el 100 % del procesador y no devolver el control por un largo tiempo, potencialmente problemático para el resto de los procesos que están funcionando en la máquina (más allá de los otros hilos de ejecución o procesos Python) y esto puede provocar el fallo del sistema operativo.

Afortunadamente, las herramientas de alto nivel que hemos vistos hasta ahora, utilizadas correctamente permiten evitar esto. Recurrir a sleep dentro de los hilos de ejecución y procesos, es costoso pero necesario para pasar el control a los otros si se necesita. Es mejor perder tiempo revisando el resto de proceso, quitar el control al que lo tiene para devolvérselo inmediatamente porque no se necesita, que perder el control de su máquina.

Este tipo de problemática, incluso simplificada, se debe dominar...