Disponemos de diversas librerías y sintaxis nativa en Python para ejecutar instrucciones de fondo, permitiendo continuar las acciones mientras por detrás se completan. De este modo evitaremos bloqueos del hilo principal o esperas innecesarias mientras esperamos una respuesta (un API, disco duro, calculo complejo…). Y entre todas las posibilidades voy a dejar un par de ejemplos de como usar threading.
¿Por qué deberíamos acostumbrarnos a usarla? Es un error clásico en el desarrollo web hacer esperar a un usuario antes de darle una respuesta. Pongamos de ejemplo un formulario de contacto: hasta que el email no se ha enviado no le decimos que todo ha ido bien, haciendo que espere sin sentido mientras conectamos con el servidor SMTP, generamos el cuerpo del correo, tal vez algún PDF, lo enviamos, almacenamos una copia en la base de datos, puede que algunas copias a los administradores, etc… ¿Por qué no dejar esa tarea en un hilo asíncrono en background? El visitante percibirá una respuesta casi instantánea de que todo ha ido bien, ganando fluidez cuando interactúe con la aplicación.
Threading
Es la manera más sencilla.
En el siguiente ejemplo lanzamos function_1
en background.
import threading
# Función que se ejecutará de forma asíncrona
def funcion_1(parametro_1):
pass
# Declaro un hilo de ejecución. "args" debe ser una Tupla.
threading_emails = threading.Thread(target=funcion_1, args=(parametro_1,))
# Lo lanzo
threading_emails.start()
# Resto de mi código que se ejecutará de forma paralela
...
Veamos un ejemplo con código real.
import threading
import time
# Función que se ejecutará de forma asíncrona
def saluda(nombre):
# Espera 2 segundos
time.sleep(2)
print(f"Hola {nombre}")
# Declaro un hilo de ejecución
threading_emails = threading.Thread(target=saluda, args=("Snake",))
# Lo lanzo
threading_emails.start()
# Resto de mi código que se ejecutará de forma paralela
print("Soy el resto del código")
Se imprimirá:
Soy el resto del código
Hola Snake
En el caso de necesitar de ejecutar muchas funciones asíncronas, podemos agruparlas en una sola.
import threading
# Funciones que quiero ejecutar de forma asíncrona
def funcion_1(parametro_1):
pass
def funcion_2():
pass
def funcion_3(parametro_2):
pass
def funcion_4(parametro_1, parametro_2):
pass
# Función que engloba a todas
def conjunto_de_funciones(parametro_1, parametro_2):
funcion_1(parametro_1)
funcion_2()
funcion_3(parametro_2)
funcion_4(parametro_1, parametro_2)
# Declaro un hilo de ejecución
threading_emails = threading.Thread(target=conjunto_de_funciones, args=(parametro_1, parametro_2))
# Lo lanzo
threading_emails.start()
# Resto de mi código que se ejecutará de forma paralela
...
Lamentablemente posee una pega: No podemos capturar el return
, o el valor, de la función. Para solucionarlo podemos utilizar una alternativa llamada futures
.
Futures
En el siguiente ejemplo dispongo de una función que deseo ejecutar de forma en paralelo y además deseo capturar sus valores: mi_funcion_de_tiempo
. Su tarea es básica, suma el primer argumento con el segundo y espera el tiempo en segundos del tercer argumento.
import time
import concurrent.futures
def mi_funcion_de_tiempo(num1, num2, time_sleep):
time.sleep(time_sleep)
return num1 + num2
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
mis_functiones_en_hilos = {
executor.submit(mi_funcion_de_tiempo, 2, 4, 5):
"La suma de 2 + 4 en 5 segundos",
executor.submit(mi_funcion_de_tiempo, 3, 5, 3):
"La suma de 3 + 5 en 3 segundos",
}
for future in concurrent.futures.as_completed(mis_functiones_en_hilos):
etiqueta = mis_functiones_en_hilos[future]
try:
if etiqueta == "La suma de 2 + 4 en 5 segundos":
print("2 + 4 = " + str(future.result()))
elif etiqueta == "La suma de 3 + 5 en 3 segundos":
print("3 + 5 = " + str(future.result()))
except Exception as exc:
print('%r generated an exception: %s' % (final, exc))
print("Terminadas")
# 3 + 5 = 8
# 2 + 4 = 6
# Terminadas
¡Atención! El ejemplo no es asíncrono. Si no terminan las funciones de ejecutarse, la lógica no continuará. En caso de necesitas paralelizar tu código asíncronamente, puedes consultar la librería asyncio
o directamente aprender a usar funciones async
.
Te deseo mucho ánimo en diferentes hilos paralelos.
{{ comments.length }} comentarios