Threading reduce significativamente el tiempo de ejecución de un programa. Aprenda a implementar subprocesos en Python.

El tiempo de ejecución es una de las medidas comunes de la eficiencia de un programa. Cuanto más rápido sea el tiempo de ejecución, mejor será el programa. Threading es una técnica que permite que un programa realice múltiples tareas o procesos simultáneamente.

Aprenderá a usar el Python incorporado enhebrar módulo y el características.concurrentes módulo. Ambos módulos ofrecen formas sencillas de crear y administrar subprocesos.

Importancia de enhebrar

La creación de subprocesos reduce la cantidad de tiempo que tarda un programa en completar un trabajo. Si el trabajo contiene varias tareas independientes, puede utilizar subprocesos para ejecutar las tareas al mismo tiempo, lo que reduce el tiempo de espera del programa para que finalice una tarea antes de pasar a la siguiente.

Por ejemplo, un programa que descargue varios archivos de imagen de Internet. Este programa puede utilizar hilos para descargar los archivos en paralelo en lugar de uno a la vez. Esto elimina el tiempo que el programa tendría que esperar para que se complete el proceso de descarga de un archivo antes de pasar al siguiente.

instagram viewer

Programa inicial antes de enhebrar

La función en el siguiente programa representa una tarea. La tarea es pausar la ejecución del programa por un segundo. El programa llama a la función dos veces, por lo que crea dos tareas. Luego calcula el tiempo que tardó en ejecutarse todo el programa y luego lo muestra en la pantalla.

importar tiempo

start_time = time.perf_counter()

definitivamentepausa():
imprimir('Durmiendo 1 segundo...')
tiempo de dormir(1)
imprimir('Terminé de dormir...')

pausa()
pausa()
finish_time = time.perf_counter()
imprimir(f'Terminado en {ronda (hora_finalización - hora_inicio, 2)} segundos)')

El resultado muestra que el programa tardó 2,01 segundos en ejecutarse. Cada tarea tardó un segundo y el resto del código tardó 0,01 segundos en ejecutarse.

Puede usar subprocesos para ejecutar simultáneamente ambas tareas. Esto tomará ambas tareas un segundo para ejecutarse.

Implementación de subprocesos mediante el módulo de subprocesos

Para modificar el código inicial para implementar subprocesos, importe el enhebrar módulo. Crear dos hilos, hilo_1 y hilo_2 utilizando el Hilo clase. Llama a comenzar en cada subproceso para iniciar su ejecución. Llama a unirse en cada subproceso para esperar a que se complete su ejecución antes de que se ejecute el resto del programa.

importar tiempo
importar enhebrar
start_time = time.perf_counter()

definitivamentepausa():
imprimir('Durmiendo 1 segundo...')
tiempo de dormir(1)
imprimir('Terminé de dormir...')

thread_1 = enhebrar. Tema (objetivo=pausa)
thread_2 = enhebrar. Tema (objetivo=pausa)

hilo_1.inicio()
hilo_2.inicio()

subproceso_1.unirse()
subproceso_2.unirse()

finish_time = time.perf_counter()
imprimir(f'Terminado en {ronda (hora_finalización - hora_inicio, 2)} segundos)')

El programa ejecutará ambos subprocesos al mismo tiempo. Esto reducirá la cantidad de tiempo que lleva realizar ambas tareas.

La salida muestra que el tiempo necesario para ejecutar las mismas tareas es de alrededor de un segundo. Esto es la mitad del tiempo que tomó el programa inicial.

Implementación de subprocesos mediante el módulo concurrent.futures

Python 3.2 vio la introducción de la futuros concurrentes módulo. Este módulo proporciona una interfaz de alto nivel para ejecutar tareas asincrónicas mediante subprocesos. Proporciona una forma más sencilla de ejecutar tareas en paralelo.

Para modificar el programa inicial para usar hilos, importe el módulo concurrent.features. Utilizar el ThreadPoolExecutor clase del módulo concurrent.futures para crear un grupo de subprocesos. Enviar el pausa función a la piscina dos veces. El entregar método devuelve un futuro objeto que representa el resultado de la llamada a la función.

Iterar sobre el futuros e imprimir sus resultados usando el resultado método.

importar tiempo
importar futuros concurrentes

start_time = time.perf_counter()

definitivamentepausa():
imprimir('Durmiendo 1 segundo...')
tiempo de dormir(1)
devolver'Terminé de dormir...'

con futuros.concurrentes. ThreadPoolExecutor() como ejecutor:
resultados = [ejecutor.enviar (pausa) para _ en rango(2)]
para F en concurrent.futures.as_completed (resultados):
imprimir (f.resultado())

finish_time = time.perf_counter()

imprimir(f'Terminado en {ronda (hora_finalización - hora_inicio, 2)} segundos)')

El módulo concurrent.features se encarga de iniciar y unir los hilos por usted. Esto hace que su código sea más limpio.

La salida es idéntica a la del módulo de roscado. El módulo de subprocesos es útil para casos simples en los que necesita ejecutar algunos subprocesos en paralelo. Por otro lado, el módulo concurrent.futures es útil para casos más complejos en los que necesita ejecutar muchas tareas al mismo tiempo.

Uso de subprocesos en un escenario del mundo real

El uso de subprocesos para ejecutar el programa anterior redujo el tiempo en un segundo. En el mundo real, los hilos ahorran más tiempo. Crea un programa que descargue imágenes de Internet. Comienza por creando un nuevo entorno virtual. Ejecute el siguiente comando en la terminal para instalar el peticiones biblioteca:

solicitudes de instalación de pip

La biblioteca de solicitudes le permitirá enviar solicitudes HTTP. Importe la biblioteca de solicitudes y la biblioteca de tiempos.

importar peticiones
importar tiempo

Cree una lista de URL de las imágenes que le gustaría descargar. Deje que sean al menos diez para que pueda notar una diferencia significativa cuando implemente subprocesos.

URL_img = [
' https://images.unsplash.com/photo-1524429656589-6633a470097c',
' https://images.unsplash.com/photo-1530224264768-7ff8c1789d79',
' https://images.unsplash.com/photo-1564135624576-c5c88640f235',
' https://images.unsplash.com/photo-1541698444083-023c97d3f4b6',
' https://images.unsplash.com/photo-1522364723953-452d3431c267',
' https://images.unsplash.com/photo-1513938709626-033611b8cc03',
' https://images.unsplash.com/photo-1507143550189-fed454f93097',
' https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e',
' https://images.unsplash.com/photo-1504198453319-5ce911bafcde',
' https://images.unsplash.com/photo-1530122037265-a5f1f91d3b99',
' https://images.unsplash.com/photo-1516972810927-80185027ca84',
' https://images.unsplash.com/photo-1550439062-609e1531270e',
]

Bucle sobre la lista de URL descargando cada imagen en la misma carpeta que contiene su proyecto. Muestre el tiempo necesario para descargar las imágenes restando la hora de finalización de la hora de inicio.

start_time = time.perf_counter()
para img_url en URL_img:
img_bytes = solicitudes.get (img_url).contenido
nombre_img = url_img.split('/')[3]
nombre_img = F'{img_nombre}.jpg'
con abrir (nombre_img, 'wb') como archivo_img:
img_file.escribir (img_bytes)
imprimir(F'{img_nombre} fue descargado...')
finish_time = time.perf_counter()
imprimir(f'Terminado en {hora_finalización - hora_inicio} segundos')

El programa tarda alrededor de 22 segundos en descargar las 12 imágenes. Puede variar para usted, ya que el tiempo necesario para descargar las imágenes también depende de la velocidad de su Internet.

Modifique el programa para usar hilos usando el módulo concurrent.features. En lugar de un bucle, use una función. Esta es la función que le pasarás al ejecutor instancia.

importar peticiones
importar tiempo
importar futuros concurrentes

URL_img = [
' https://images.unsplash.com/photo-1524429656589-6633a470097c',
' https://images.unsplash.com/photo-1530224264768-7ff8c1789d79',
' https://images.unsplash.com/photo-1564135624576-c5c88640f235',
' https://images.unsplash.com/photo-1541698444083-023c97d3f4b6',
' https://images.unsplash.com/photo-1522364723953-452d3431c267',
' https://images.unsplash.com/photo-1513938709626-033611b8cc03',
' https://images.unsplash.com/photo-1507143550189-fed454f93097',
' https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e',
' https://images.unsplash.com/photo-1504198453319-5ce911bafcde',
' https://images.unsplash.com/photo-1530122037265-a5f1f91d3b99',
' https://images.unsplash.com/photo-1516972810927-80185027ca84',
' https://images.unsplash.com/photo-1550439062-609e1531270e',
]

start_time = time.perf_counter()

definitivamentedescargar_imagen(img_url):
img_bytes = solicitudes.get (img_url).contenido
nombre_img = url_img.split('/')[3]
nombre_img = F'{img_nombre}.jpg'
con abrir (nombre_img, 'wb') como archivo_img:
img_file.escribir (img_bytes)
imprimir(F'{img_nombre} fue descargado...')

con futuros.concurrentes. ThreadPoolExecutor() como ejecutor:
ejecutor.map (download_image, img_urls)

finish_time = time.perf_counter()

imprimir(f'Terminado en {hora_de_finalización-hora_de_inicio} segundos')

Después de introducir el hilo. El tiempo se reduce significativamente. Solo tomó 4 segundos completar la ejecución del programa.

Escenarios adecuados para subprocesos

Algunos de los escenarios adecuados para subprocesos son:

  • Tareas enlazadas de E/S: si el programa pasa la mayor parte del tiempo esperando que se completen las operaciones de entrada o salida. La creación de subprocesos puede mejorar el rendimiento al permitir que se ejecuten otras tareas mientras se espera que se completen las operaciones de E/S.
  • raspado web: El raspado web implica realizar solicitudes HTTP y analizar respuestas HTML. Threading ayuda a acelerar el proceso al permitirle realizar múltiples solicitudes simultáneamente.
  • Tareas vinculadas a la CPU: Threading puede ayudar a mejorar el rendimiento al permitir que varias tareas se ejecuten en paralelo.

Familiarícese con Threading en otros idiomas

Python no es el único lenguaje que admite subprocesos. La mayoría de los lenguajes de programación admiten alguna forma de subprocesamiento. Es importante familiarizarse con la implementación de hilos en otros idiomas. Esto lo equipa con las habilidades necesarias para abordar diferentes escenarios en los que se pueden aplicar subprocesos.