Diferencia en aspectos de concurrencia en Python
Uno puede imaginar que Python acaba de introducir estas nociones o capacidades, dado que estamos escuchando muchas tendencias nuevas con respecto a las operaciones asincrónicas y la concurrencia con el lanzamiento de Python 3.
Muchos recién llegados podrían creer que usar asyncio
es el único enfoque práctico para realizar actividades concurrentes y asíncronas. Este artículo discutirá cómo podemos lograr la concurrencia y sus ventajas o desventajas en Python.
Hilos y multihilos
Los hilos han estado en Python durante mucho tiempo. Como resultado, podemos realizar múltiples operaciones a la vez gracias a los hilos.
Desafortunadamente, CPython
, una versión típica de Python de la línea principal, todavía usa el bloqueo de intérprete global (GIL
), lo que hace que las aplicaciones de subprocesos múltiples, el método común de hoy en día para implementar el procesamiento paralelo, no sean ideales.
Python introdujo GIL
para hacer que la memoria de CPython maneje integraciones más manejables con C (por ejemplo, las extensiones).
El GIL
es un mecanismo de bloqueo en el que el intérprete de Python ejecuta solo un hilo simultáneamente. El código byte
de Python solo puede ser ejecutado por un subproceso simultáneamente.
Código de ejemplo:
import threading
import time
import random
def worker(num):
sec = random.randrange(1, 5)
time.sleep(sec)
print("I am thread {}, who slept for {} seconds.".format(num, sec))
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
t.start()
print("Completed!")
Producción :
Completed!
I am thread 1, who slept for 3 seconds.
I am thread 3, who slept for 2 seconds.
I am thread 4, who slept for 4 seconds.
Procesos y Multiprocesamiento
El multiprocesamiento hace uso de muchas CPU. Podemos realizar varias tareas simultáneamente de manera efectiva ya que cada CPU opera en paralelo. Para los trabajos que están vinculados a la CPU, el multiprocesamiento es lo que desea utilizar.
Python introduce el módulo de multiprocesamiento
para lograr el paralelismo, que se sentirá muy similar si ha utilizado subprocesos.
Código de ejemplo:
import multiprocessing
import time
import random
def worker(num):
sec = random.randrange(1, 5)
time.sleep(sec)
print("I am process {}, who slept for {} seconds.".format(num, sec))
for i in range(3):
t = multiprocessing.Process(target=worker, args=(i,))
t.start()
print("Completed")
Producción :
Completed
I am process 1, who slept for 1 seconds.
I am process 2, who slept for 2 seconds.
I am process 0, who slept for 3 seconds.
En lugar de subprocesos múltiples, usamos múltiples procesos que se ejecutan en diferentes núcleos de su CPU, lo que hace que nuestro script de Python sea más rápido.
Asíncrono y asyncio
En las operaciones síncronas, las tareas se realizan de forma sincronizada, una tras otra. Sin embargo, los trabajos pueden comenzar en operaciones asincrónicas con total independencia entre sí.
Una tarea asincrónica
puede comenzar y continuar ejecutándose mientras la ejecución cambia a otra actividad. Por otro lado, las tareas asincrónicas a menudo se ejecutan en segundo plano y no se bloquean (hacen que la implementación espere a que se complete).
Junto con otras características valiosas, asyncio
ofrece un ciclo de eventos. El bucle de eventos supervisa varios eventos de E/S, cambia a tareas preparadas y pausa las tareas que esperan E/S.
Como resultado, no perdemos el tiempo en proyectos sin terminar.
Código de ejemplo:
import asyncio
import datetime
import random
async def my_sleep_func():
await asyncio.sleep(random.randint(0, 5))
async def displayDate(num, loop):
endTime = loop.time() + 60.0
while True:
print("Loop: {} Time: {}".format(num, datetime.datetime.now()))
if (loop.time() + 1.0) >= endTime:
break
await my_sleep_func()
loop = asyncio.get_event_loop()
asyncio.ensure_future(displayDate(1, loop))
asyncio.ensure_future(displayDate(2, loop))
loop.run_forever()
Si repasamos el fragmento de código anterior:
- Tenemos una función
async
displayDate
, que toma como parámetros un número y el bucle de eventos. - Dicha función tiene un bucle infinito que se detiene después de 60 segundos. Pero durante estos 60 segundos, imprime repetidamente la hora y toma una siesta.
- La función
await
puede esperar a que se completen otras funcionesasync
. - Pasamos la función al bucle de eventos (usando la función
ensure_future
). - Comenzamos a ejecutar el bucle de eventos.
Cada vez que se realiza la llamada await
, asyncio
entiende que la función probablemente necesitará algo de tiempo. Cuando asyncio
nota que la E/S de la función detenida está lista, reanuda el proceso.
Ahora, el punto es, ¿qué necesitamos usar entre las tres formas de concurrencia? Podemos tomar nota de lo siguiente para ayudarnos en nuestra toma de decisiones:
- Utilice el multiprocesamiento para las operaciones vinculadas a la CPU.
- Utilice subprocesos múltiples para E/S limitada, E/S rápida y número limitado de conexiones.
- Use E/S asíncrona para E/S limitada, E/S lenta y muchas conexiones.
asyncio/await
funciona en Python 3.5 y versiones posteriores.
También podemos referirnos al pseudocódigo a continuación:
if io_bound:
if io_very_slow:
print("Use asyncio")
else:
print("Use multithreading")
else:
print("multiprocessing")
Marion specializes in anything Microsoft-related and always tries to work and apply code in an IT infrastructure.
LinkedInArtículo relacionado - Python Threading
- Obtenga un valor de retorno de un hilo en Python
- Clase Timer en el módulo Threading en Python
- Diferencia entre multiprocesamiento y subprocesamiento en Python
- Threadpool de Python