Hilo fácil de usar

PythonPythonIntermediate
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este tutorial, aprenderemos a usar el módulo threading de Python para ejecutar múltiples hilos de ejecución de manera concurrente.

El módulo threading de Python proporciona una forma sencilla de crear y gestionar hilos en un programa de Python. Un hilo es un flujo de ejecución independiente dentro de un programa. Al ejecutar múltiples hilos concurrentemente, podemos aprovechar los procesadores multi-core y mejorar el rendimiento de nuestros programas.

El módulo threading proporciona dos clases para crear y gestionar hilos:

  1. Clase Thread: Esta clase representa un solo hilo de ejecución.
  2. Clase Lock: Esta clase permite la sincronización del acceso a recursos compartidos entre hilos.

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ModulesandPackagesGroup -.-> python/creating_modules("Creating Modules") python/ModulesandPackagesGroup -.-> python/standard_libraries("Common Standard Libraries") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/AdvancedTopicsGroup -.-> python/threading_multiprocessing("Multithreading and Multiprocessing") subgraph Lab Skills python/function_definition -.-> lab-7839{{"Hilo fácil de usar"}} python/creating_modules -.-> lab-7839{{"Hilo fácil de usar"}} python/standard_libraries -.-> lab-7839{{"Hilo fácil de usar"}} python/classes_objects -.-> lab-7839{{"Hilo fácil de usar"}} python/threading_multiprocessing -.-> lab-7839{{"Hilo fácil de usar"}} end

Creando hilos

Para crear un nuevo hilo en Python, necesitamos crear una nueva instancia de la clase Thread y pasarle una función a ejecutar.

Crea un proyecto llamado create_thread.py en el WebIDE y escribe el siguiente contenido.

import threading

## Define una función para ejecutar en el hilo
def my_function():
    print("Hello from thread")

## Crea un nuevo hilo
thread = threading.Thread(target=my_function)

## Inicia el hilo
thread.start()

## Espera a que el hilo termine
thread.join()

## Imprime "Hecho" para indicar que el programa ha terminado
print("Done")

Este ejemplo define una función my_function que imprime un mensaje. Luego creamos una nueva instancia de la clase Thread, pasándole my_function como función objetivo. Finalmente, iniciamos el hilo usando el método start y esperamos a que termine usando el método join.

Utiliza el siguiente comando para ejecutar el script.

python create_thread.py

Sincronización

Si múltiples hilos acceden al mismo recurso compartido (por ejemplo, una variable o un archivo), debemos sincronizar el acceso a ese recurso para evitar condiciones de carrera. El módulo threading de Python proporciona una clase Lock para este propósito.

Aquí hay un ejemplo de cómo usar un Lock para crear un proyecto llamado sync.py en el WebIDE y escribir el siguiente contenido.

import threading

## Crea un candado para proteger el acceso al recurso compartido
lock = threading.Lock()

## Recurso compartido que será modificado por múltiples hilos
counter = 0

## Define una función que cada hilo ejecutará
def my_function():
    global counter

    ## Adquiere el candado antes de acceder al recurso compartido
    lock.acquire()
    try:
        ## Accede al recurso compartido
        counter += 1
    finally:
        ## Libera el candado después de modificar el recurso compartido
        lock.release()

## Crea múltiples hilos para acceder al recurso compartido
threads = []

## Crea y comienza 10 hilos que ejecutan la misma función
for i in range(10):
    thread = threading.Thread(target=my_function)
    threads.append(thread)
    thread.start()

## Espera a que todos los hilos terminen su ejecución
for thread in threads:
    thread.join()

## Imprime el valor final del contador
print(counter) ## Salida: 10

En este ejemplo, creamos un objeto Lock y un recurso compartido counter. La función my_function accede al recurso compartido adquiriendo el candado con el método acquire y liberando el candado con el método release. Creamos múltiples hilos y los iniciamos, luego los esperamos a que terminen con el método join. Finalmente, imprimimos el valor final del contador.

Utiliza el siguiente comando para ejecutar el script.

python sync.py

Hilo con argumentos

En Python, puedes pasar argumentos a los hilos ya sea utilizando el parámetro args al crear un nuevo hilo o mediante la subclase de la clase Thread y la definición de tu constructor que acepta argumentos. Aquí hay ejemplos de ambos enfoques:

1; Creamos una subclase de la clase Thread y reemplazamos el método run para definir el comportamiento del hilo. Crea un proyecto llamado thread_subclass.py en el WebIDE y escribe el siguiente contenido.

import threading

## Define una clase personalizada de hilo que extiende la clase Thread
class MyThread(threading.Thread):
    ## Reemplaza el método run() para implementar el comportamiento del hilo
    def run(self):
        print("Hello from thread")

## Crea una instancia de la clase personalizada de hilo
thread = MyThread()

## Inicia el hilo
thread.start()

## Espera a que el hilo termine su ejecución
thread.join()

Utiliza el siguiente comando para ejecutar el script.

python thread_subclass.py

2; Creamos un hilo y pasamos argumentos a la función objetivo utilizando el parámetro args. Crea un proyecto llamado thread_with_args.py en el WebIDE y escribe el siguiente contenido

import threading

## Define una función que toma un parámetro
def my_function(name):
    print("Hello from", name)

## Crea un nuevo hilo con función objetivo y argumentos
thread = threading.Thread(target=my_function, args=("Thread 1",))

## Inicia el hilo
thread.start()

## Espera a que el hilo termine su ejecución
thread.join()

Utiliza el siguiente comando para ejecutar el script.

python thread_with_args.py

Pool de hilos

En Python, puedes usar un pool de hilos para ejecutar tareas concurrentemente utilizando un conjunto predefinido de hilos. La ventaja de usar un pool de hilos es que evita la sobrecarga de crear y destruir hilos para cada tarea, lo que puede mejorar el rendimiento.

El módulo concurrent.futures de Python proporciona una clase ThreadPoolExecutor que te permite crear un pool de hilos y enviar tareas. Aquí hay un ejemplo:

Crea un proyecto llamado thread_pool_range.py en el WebIDE y escribe el siguiente contenido.

import concurrent.futures

## Define una función que se ejecutará en múltiples hilos con dos argumentos
def my_func(arg1, arg2):
    ## Define las tareas realizadas por el hilo aquí
    print(f"Hello from my thread with args {arg1} and {arg2}")

## Crea un objeto ThreadPoolExecutor con un máximo de 5 hilos trabajadores
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    ## Envía cada tarea (llamada a función con sus argumentos) al ejecutor para su procesamiento en un hilo separado
    ## El método submit() devuelve un objeto Future que representa el resultado de la computación asíncrona
    for i in range(10):
        executor.submit(my_func, i, i+1)

En este ejemplo, definimos una función my_func que toma dos argumentos. Creamos un ThreadPoolExecutor con un máximo de 5 hilos trabajadores. Luego, recorremos un rango de números y enviamos tareas al pool de hilos usando el método executor.submit(). Cada tarea enviada se ejecuta en uno de los hilos trabajadores disponibles.

Consejos: El objeto ThreadPoolExecutor se utiliza como administrador de contexto. Esto garantiza que todos los hilos se limpien correctamente cuando se completa el código dentro del bloque with.

Utiliza el siguiente comando para ejecutar el script.

python thread_pool_range.py

El método submit() devuelve inmediatamente un objeto Future, que representa el resultado de la tarea enviada. Puedes usar el método result() del objeto Future para recuperar el valor de retorno de la tarea. Si la tarea lanza una excepción, llamar a result() lanzará esa excepción.

También puedes usar el método map() de la clase ThreadPoolExecutor para aplicar la misma función a una colección de elementos. Por ejemplo, crea un proyecto llamado thread_pool_map.py en el WebIDE y escribe el siguiente contenido.:

import concurrent.futures

## Define una función que se ejecutará en múltiples hilos
def my_func(item):
    ## Define las tareas realizadas por el hilo aquí
    print(f"Hello from my thread with arg {item}")

## Crea una lista de elementos que serán procesados por los hilos
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

## Crea un objeto ThreadPoolExecutor con un máximo de 5 hilos trabajadores
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    ## Envía cada elemento al ejecutor para su procesamiento en un hilo separado
    ## El método map() devuelve automáticamente los resultados en orden
    executor.map(my_func, items)

En este ejemplo, definimos una función my_func que toma un argumento. Creamos una lista de elementos y los enviamos al pool de hilos usando el método executor.map(). Cada elemento de la lista se pasa a my_func como un argumento, y cada elemento se ejecuta en uno de los hilos trabajadores disponibles.

Utiliza el siguiente comando para ejecutar el script.

python thread_pool_map.py

Los resultados obtenidos de thread_pool_range.py y thread_pool_map.py son los mismos.

Hilos demonio

En Python, un hilo demonio es un tipo de hilo que se ejecuta en segundo plano y no impide que el programa salga. Cuando todos los hilos no demonio se han completado, el intérprete de Python sale, independientemente de si algún hilo demonio sigue en ejecución.

Crea un proyecto llamado daemon_thread_with_args.py en el WebIDE y escribe el siguiente contenido.

import threading
import time

## Define una función que se ejecuta indefinidamente e imprime mensajes a intervalos regulares
def my_function():
    while True:
        print("Hello from thread")
        time.sleep(1)

## Crea un hilo demonio que ejecuta la función objetivo
thread = threading.Thread(target=my_function, daemon=True)

## Inicia el hilo
thread.start()

## El programa principal continúa ejecutándose e imprime un mensaje
print("Main program")

## Espera unos segundos antes de salir del programa
time.sleep(5)

## El programa sale y el hilo demonio se termina automáticamente
print("Main thread exiting...")

En este ejemplo, creamos un hilo que ejecuta un bucle infinito e imprime un mensaje cada segundo usando la función time.sleep(). Marcamos el hilo como demonio usando el parámetro daemon para que salga cuando el programa principal sale automáticamente. El programa principal continúa ejecutándose e imprime un mensaje. Esperamos unos segundos y el programa sale, terminando el hilo demonio.

Luego utiliza el siguiente comando para ejecutar el script.

python daemon_thread_with_args.py

Por supuesto, también podemos establecer un hilo como demonio llamando al método setDaemon(True) en la instancia del hilo. Por ejemplo, crea un proyecto llamado daemon_thread_with_func.py en el WebIDE y escribe el siguiente contenido:

import threading
import time

## Define una función que se ejecuta indefinidamente e imprime mensajes a intervalos regulares
def my_function():
    while True:
        print("Hello from thread")
        time.sleep(1)

## Crea un nuevo hilo con función objetivo
thread = threading.Thread(target=my_function)

## Establece la bandera de demonio en True para que el hilo se ejecute en segundo plano y se termine cuando el programa principal sale
thread.setDaemon(True)

## Inicia el hilo
thread.start()

## El programa principal continúa ejecutándose e imprime un mensaje
print("Main program")

## Espera unos segundos antes de salir del programa
time.sleep(5)

## El programa sale y el hilo demonio se termina automáticamente
print("Main thread exiting...")

Ejecutar el script con el siguiente comando producirá el mismo resultado que el ejemplo anterior.

python daemon_thread_with_func.py

Objeto Evento

En Python, puedes usar el objeto threading.Event para permitir que los hilos esperen a que ocurra un evento específico antes de continuar. El objeto Event proporciona una forma para que un hilo emita una señal de que un evento ha ocurrido, y otros hilos pueden esperar esa señal.

Crea un proyecto llamado event_object.py en el WebIDE y escribe el siguiente contenido.

import threading

## Crea un objeto Event
event = threading.Event()

## Define una función que espera a que el evento se active
def my_function():
    print("Esperando evento")
    ## Espera a que el evento se active
    event.wait()
    print("Evento recibido")

## Crea un nuevo hilo con función objetivo
thread = threading.Thread(target=my_function)

## Inicia el hilo
thread.start()

## Señala el evento después de unos segundos
## La llamada a wait() en la función objetivo ahora devolverá y continuará la ejecución
event.set()

## Espera a que el hilo termine de ejecutarse
thread.join()

En este ejemplo, creamos un objeto Event usando la clase Event. Definimos una función que espera a que se emita una señal del evento usando el método wait y luego imprime un mensaje. Creamos un nuevo hilo y lo iniciamos. Después de unos segundos, señalamos el evento usando el método set. El hilo recibe el evento e imprime un mensaje. Finalmente, esperamos a que el hilo termine usando el método join.

Luego utiliza el siguiente comando para ejecutar el script.

python event_object.py

Objeto Timer

En Python, puedes usar el objeto threading.Timer para programar una función para que se ejecute después de transcurrir un tiempo específico. El objeto Timer crea un nuevo hilo que espera el intervalo de tiempo especificado antes de ejecutar la función.

Crea un proyecto llamado timer_object.py en el WebIDE y escribe el siguiente contenido.

import threading

## Define una función que será ejecutada por el Timer después de 5 segundos
def my_function():
    print("Hello from timer")

## Crea un temporizador que ejecuta la función objetivo después de 5 segundos
timer = threading.Timer(5, my_function)

## Inicia el temporizador
timer.start()

## Espera a que el temporizador termine
timer.join()

En este ejemplo, creamos un objeto Timer usando la clase Timer y le pasamos un retraso de tiempo en segundos y una función a ejecutar. Iniciamos el temporizador usando el método start y esperamos a que termine usando el método join. Después de 5 segundos, la función se ejecuta e imprime un mensaje.

Luego utiliza el siguiente comando para ejecutar el script.

python timer_object.py

Consejos: El hilo del temporizador se ejecuta por separado, por lo que es posible que no esté sincronizado con el hilo principal. Si tu función depende de algún estado o recursos compartidos, necesitarás sincronizar el acceso a ellos adecuadamente. Además, recuerda que el hilo del temporizador no detendrá el programa de salir si todavía está en ejecución cuando todos los demás hilos no demonio se han completado.

Objeto Barrera

En Python, puedes usar el objeto threading.Barrier para sincronizar múltiples hilos en puntos de sincronización predefinidos. El objeto Barrera proporciona una forma para que un conjunto de hilos espere mutuamente a alcanzar un cierto punto en su ejecución antes de continuar.

Crea un proyecto llamado barrier_object.py en el WebIDE y escribe el siguiente contenido.

import threading

## Crea un objeto Barrera para 3 hilos
barrier = threading.Barrier(3)

## Define una función que espera en la barrera
def my_function():
    print("Antes de la barrera")
    ## Espera a que los tres hilos lleguen a la barrera
    barrier.wait()
    print("Después de la barrera")

## Crea 3 hilos usando un bucle y los inicia
threads = []
for i in range(3):
    thread = threading.Thread(target=my_function)
    threads.append(thread)
    thread.start()

## Espera a que todos los hilos terminen de ejecutarse
for thread in threads:
    thread.join()

En este ejemplo, creamos un objeto Barrier usando la clase Barrier y le pasamos el número de hilos que deben esperar. Usando el método wait, definimos una función que espera en la barrera e imprime un mensaje. Creamos tres hilos y los iniciamos. Cada hilo espera en la barrera, por lo que todos los hilos esperarán hasta que todos hayan llegado a la barrera. Finalmente, esperamos a que todos los hilos terminen usando el método join.

Luego utiliza el siguiente comando para ejecutar el script.

python barrier_object.py

Resumen

¡Eso es todo! Ahora sabes cómo usar el módulo threading de Python en tu código. Esto puede ayudarnos a dominar en profundidad los principios y técnicas básicas de la programación concurrente para que podamos desarrollar mejores aplicaciones concurrentes eficientes.