subprocesos mĂșltiples en Python con ejemplo: Aprenda GIL en Python

⚡ Resumen inteligente

subprocesos mĂșltiples en Python Este mĂ©todo ejecuta varios hilos dentro de un mismo proceso, de modo que comparten memoria y trabajan simultĂĄneamente. El mĂłdulo de subprocesos crea y gestiona estos hilos, mientras que el bloqueo del intĂ©rprete global limita el paralelismo real, lo que hace que esta tĂ©cnica sea mĂĄs adecuada para tareas con alta dependencia de entrada/salida.

  • 🔘 Hilo vs. proceso: Un hilo es una unidad de ejecuciĂłn dentro de un proceso, y los hilos comparten los datos del proceso mientras se ejecutan individualmente.
  • ☑ Dos mĂłdulos: El mĂłdulo obsoleto _thread ofrece llamadas de bajo nivel, mientras que el mĂłdulo threading de nivel superior es el estĂĄndar moderno para la creaciĂłn de hilos.
  • ✅ Crear hilos: Extiende la clase Thread, sobrescribe __init__ y run(), luego llama a start() para iniciar y a join() para esperar a que finalice.
  • đŸ§Ș Evite las condiciones de carrera: Un bloqueo permite que solo un hilo acceda a una secciĂłn crĂ­tica a la vez, evitando asĂ­ interbloqueos y la corrupciĂłn de datos compartidos.
  • đŸ› ïž Entienda el GIL: El bloqueo del intĂ©rprete global permite que un hilo se ejecute Python El procesamiento de bytes se realiza de uno en uno, por lo que las tareas que dependen de la CPU requieren multiprocesamiento.
  • đŸ€– Cargas de trabajo de IA: El multihilo acelera los pasos de IA que dependen de la entrada/salida, como la carga de datos, mientras que NumPy y PyTorch libera el GIL para computaciĂłn paralela.

subprocesos mĂșltiples en Python

El Python El lenguaje de programaciĂłn te permite usar multiprocesamiento o multihilo. En este tutorial, aprenderĂĄs cĂłmo escribir aplicaciones multihilo en Python.

¿Qué es un hilo?

Un hilo es una unidad de ejecución en la programación concurrente. La multihilo es una técnica que permite a una CPU ejecutar varias tareas de un mismo proceso simultåneamente. Estos hilos pueden ejecutarse de forma independiente, compartiendo los recursos del proceso.

¿Qué es un proceso?

Un proceso es bĂĄsicamente el programa en ejecuciĂłn. Cuando inicias una aplicaciĂłn en tu ordenador (como un navegador o un editor de texto), el sistema operativo crea un .

ÂżQuĂ© es el subproceso mĂșltiple en Python?

subprocesos mĂșltiples en Python La programaciĂłn multihilo es una tĂ©cnica bien conocida en la que mĂșltiples hilos de un proceso comparten su espacio de datos con el hilo principal, lo que facilita y agiliza el intercambio de informaciĂłn y la comunicaciĂłn entre hilos. Los hilos son mĂĄs ligeros que los procesos. Varios hilos pueden ejecutarse individualmente compartiendo sus recursos. El objetivo de la programaciĂłn multihilo es ejecutar mĂșltiples tareas y funciones simultĂĄneamente.

¿Qué es el multiprocesamiento?

Multiprocesamiento Permite ejecutar varios procesos no relacionados simultåneamente. Estos procesos no comparten sus recursos y se comunican a través de IPC.

Python MĂșltiples subprocesos frente a multiprocesamiento

Para comprender los procesos y los subprocesos, considere el siguiente escenario: Un archivo .exe en su computadora es un programa. Al abrirlo, el sistema operativo lo carga en la memoria y la CPU lo ejecuta. La instancia del programa que se estĂĄ ejecutando se denomina proceso.

Todo proceso tiene dos componentes fundamentales:

  • El Code
  • Los datos

Ahora, un proceso puede contener una o mĂĄs subpartes llamadas roscas. Esto depende de la arquitectura del sistema operativo. Se puede pensar en un hilo como una secciĂłn del proceso que el sistema operativo puede ejecutar de forma independiente.

En otras palabras, se trata de una secuencia de instrucciones que el sistema operativo puede ejecutar de forma independiente. Los hilos dentro de un mismo proceso comparten los datos de dicho proceso y estån diseñados para trabajar conjuntamente y facilitar el paralelismo.

ÂżPor quĂ© utilizar subprocesos mĂșltiples?

El multihilo permite dividir una aplicaciĂłn en varias subtareas y ejecutarlas simultĂĄneamente. Si utiliza el multihilo correctamente, podrĂĄ mejorar la velocidad, el rendimiento y la representaciĂłn de su aplicaciĂłn.

Python multihilo

Python Admite construcciones tanto para multiprocesamiento como para multihilo. En este tutorial, se centrarĂĄ principalmente en la implementaciĂłn. multiproceso aplicaciones con Python. Hay dos mĂłdulos principales que se pueden usar para manejar hilos en Python:

  1. El thread mĂłdulo, y
  2. El enhebrar mĂłdulo

Sin embargo, en PythonTambién existe algo llamado bloqueo global del intérprete (GIL). No permite mucha mejora del rendimiento e incluso puede sustancialmente el rendimiento de algunas aplicaciones multiproceso. Aprenderå todo al respecto en las próximas secciones de este tutorial.

Los mĂłdulos Thread y Threading

Los dos mĂłdulos que aprenderĂĄ en este tutorial son los mĂłdulo de hilo y conectar mĂłdulo de roscado.

Sin embargo, el mĂłdulo de subprocesos ha quedado obsoleto durante mucho tiempo. comenzando con Python 3, ha sido designado como obsoleto y sĂłlo es accesible como _hilo para compatibilidad con versiones anteriores.

DeberĂ­as usar el nivel superior. enhebrar MĂłdulo para las aplicaciones que planea implementar. El mĂłdulo thread se ha tratado aquĂ­ Ășnicamente con fines didĂĄcticos.

El mĂłdulo de hilo

La sintaxis para crear un nuevo hilo usando este mĂłdulo es la siguiente:

thread.start_new_thread(function_name, arguments)

Muy bien, ahora has cubierto la teorĂ­a bĂĄsica para comenzar a codificar. Entonces, abre tu IDLE o un bloc de notas y escribe lo siguiente:

import time
import _thread

def thread_test(name, wait):
   i = 0
   while i <= 3:
      time.sleep(wait)
      print("Running %s\n" %name)
      i = i + 1

   print("%s has finished execution" %name)

if __name__ == "__main__":
    
    _thread.start_new_thread(thread_test, ("First Thread", 1))
    _thread.start_new_thread(thread_test, ("Second Thread", 2))
    _thread.start_new_thread(thread_test, ("Third Thread", 3))

Guarde el archivo y presione F5 para ejecutar el programa. Si todo se hizo correctamente, este es el resultado que deberĂ­a ver:

El mĂłdulo de hilo

En las prĂłximas secciones aprenderĂĄs mĂĄs sobre las condiciones de la carrera y cĂłmo afrontarlas.

El mĂłdulo de hilo

EXPLICACIÓN DEL CÓDIGO

  1. Estas declaraciones importan el mĂłdulo de tiempo y de subprocesos, que se utilizan para gestionar la ejecuciĂłn y el retraso de las operaciones. Python roscas.
  2. Aquí, ha definido una función llamada prueba_hilo, que serå llamado por el inicio_nuevo_hilo Método. La función ejecuta un bucle while durante cuatro iteraciones e imprime el nombre del hilo que la llamó. Una vez completada la iteración, imprime un mensaje que indica que el hilo ha finalizado su ejecución.
  3. Esta es la sección principal de su programa. Aquí simplemente llama al inicio_nuevo_hilo método con el prueba_hilo Se pasa la función como argumento. Esto crearå un nuevo hilo para la función y comenzarå a ejecutarla. Tenga en cuenta que puede reemplazar `thread_test` con cualquier otra función que desee ejecutar como un hilo.

El mĂłdulo de enhebrado

Este mĂłdulo es la implementaciĂłn de alto nivel de subprocesos en Python y el estĂĄndar de facto para la gestiĂłn de aplicaciones multihilo. Ofrece una amplia gama de funcionalidades en comparaciĂłn con el mĂłdulo de hilos.

Estructura del mĂłdulo Threading

Estructura del mĂłdulo Threading

AquĂ­ hay una lista de algunas funciones Ăștiles definidas en este mĂłdulo:

Nombre de la funciĂłn Mareas Ideales para Lecciones
cuentaActiva() Devuelve el conteo de Hilo objetos que aĂșn estĂĄn vivos.
subproceso actual() Devuelve el objeto actual de la clase Thread.
enumerar() Enumera todos los objetos Thread activos.
es demonio() Devuelve verdadero si el hilo es un demonio.
isAlive () Devuelve verdadero si el hilo aĂșn estĂĄ vivo.
Métodos de clase de hilo
comienzo() Inicia la actividad de un hilo. Se debe llamar solo una vez para cada subproceso porque generarĂĄ un error de ejecuciĂłn si se llama varias veces.
correr() Este método denota la actividad de un subproceso y puede ser anulado por una clase que extienda la clase Thread.
unirse() Bloquea la ejecución de otro código hasta que finaliza el hilo en el que se llamó al método join().

Historia de fondo: la clase de hilo

Antes de comenzar a codificar programas multihilo utilizando el mĂłdulo threading, es crucial comprender la clase Thread. La clase thread es la clase principal que define la plantilla y las operaciones de un hilo en Python.

La forma mĂĄs comĂșn de crear un multihilo Python La aplicaciĂłn consiste en declarar una clase que extienda la clase Thread y sobrescriba su mĂ©todo run().

La clase Thread, en resumen, significa una secuencia de cĂłdigo que se ejecuta en un thread de control.

Entonces, al escribir una aplicaciĂłn multiproceso, harĂĄs lo siguiente:

  1. define una clase que extiende la clase Thread
  2. Anular el __init__ constructor
  3. Anular el correr() Método

Una vez que se ha creado un objeto de hilo, el comienzo() El método puede utilizarse para iniciar la ejecución de esta actividad, y el unirse() El método se puede utilizar para bloquear todo el resto del código hasta que finalice la actividad actual.

Ahora, intentemos usar el mĂłdulo threading para implementar su ejemplo anterior. Nuevamente, inicie su IDLE y escribe lo siguiente:

import time
import threading

class threadtester (threading.Thread):
    def __init__(self, id, name, i):
       threading.Thread.__init__(self)
       self.id = id
       self.name = name
       self.i = i
       
    def run(self):
       thread_test(self.name, self.i, 5)
       print ("%s has finished execution " %self.name)

def thread_test(name, wait, i):

    while i:
       time.sleep(wait)
       print ("Running %s \n" %name)
       i = i - 1

if __name__=="__main__":
    thread1 = threadtester(1, "First Thread", 1)
    thread2 = threadtester(2, "Second Thread", 2)
    thread3 = threadtester(3, "Third Thread", 3)

    thread1.start()
    thread2.start()
    thread3.start()

    thread1.join()
    thread2.join()
    thread3.join()

Este serĂĄ el resultado cuando ejecutes el cĂłdigo anterior:

Historia de fondo: la clase de hilo

EXPLICACIÓN DEL CÓDIGO

Historia de fondo: la clase de hilo

  1. Esta parte es igual que nuestro ejemplo anterior. AquĂ­, importas el mĂłdulo de tiempo y el mĂłdulo de hilos, que se utilizan para manejar la ejecuciĂłn y los retrasos de la Python roscas.
  2. En este bit, estås creando una clase llamada threadtester, que hereda o extiende el Hilo clase del módulo threading. Esta es una de las formas mås comunes de crear hilos en Python. Sin embargo, solo debe sobrescribir el constructor y el correr() método en su aplicación. Como puede ver en el ejemplo de código anterior, el __init__ El método (constructor) ha sido anulado. Del mismo modo, también ha anulado el correr() método. Contiene el código que desea ejecutar dentro de un hilo. En este ejemplo, ha llamado a la función thread_test().
  3. Este es el método thread_test(), que toma el valor de i como argumento, lo disminuye en 1 en cada iteración y recorre el resto del código hasta que i se convierte en 0. En cada iteración, imprime el nombre del hilo que se estå ejecutando actualmente y espera durante wait segundos (que también se toma como argumento).
  4. thread1 = threadtester(1, “First Thread”, 1) Aquí, estamos creando un hilo y pasando los tres parámetros que declaramos en __init__. El primer parámetro es la identificación del hilo, el segundo parámetro es el nombre del hilo y el tercer parámetro es el contador, que determina cuántas veces debe ejecutarse el ciclo while.
  5. thread2.start() El método start se utiliza para iniciar la ejecución de un hilo. Internamente, la función start() llama al método run() de su clase.
  6. thread3.join() El método join() bloquea la ejecución de otro código y espera hasta que finalice el hilo en el que fue llamado.

Como ya sabrĂĄs, los hilos que pertenecen al mismo proceso tienen acceso a la memoria y los datos de dicho proceso. Por lo tanto, si mĂĄs de un hilo intenta modificar o acceder a los datos simultĂĄneamente, pueden producirse errores.

En la siguiente secciĂłn, verĂĄ los diferentes tipos de complicaciones que pueden aparecer cuando los subprocesos acceden a los datos y la secciĂłn crĂ­tica sin comprobar si existen transacciones de acceso.

Puntos muertos y condiciones de carrera

Antes de aprender sobre interbloqueos y condiciones de carrera, serĂĄ Ăștil comprender algunas definiciones bĂĄsicas relacionadas con la programaciĂłn concurrente:

  • SecciĂłn crĂ­tica: Se trata de un fragmento de cĂłdigo que accede o modifica variables compartidas y debe ejecutarse como una transacciĂłn atĂłmica.
  • Cambio de contexto: Es el proceso que sigue una CPU para almacenar el estado de un hilo antes de cambiar de una tarea a otra, de modo que pueda reanudarse desde el mismo punto mĂĄs adelante.

Puntos muertos

Puntos muertos son el problema mĂĄs temido al que se enfrentan los desarrolladores al escribir aplicaciones concurrentes/multihilo en PythonLa mejor manera de entender los interbloqueos es utilizando el problema clĂĄsico de la informĂĄtica conocido como el GastronomĂ­a PhiloProblema de Sophers.

El planteamiento del problema para los filĂłsofos comedores es el siguiente:

Cinco filĂłsofos estĂĄn sentados alrededor de una mesa redonda con cinco platos de espaguetis (un tipo de pasta) y cinco tenedores, como se muestra en el diagrama.

GastronomĂ­a Philoproblema de sophers

GastronomĂ­a Philoproblema de sophers

En cualquier momento dado, un filĂłsofo debe estar comiendo o pensando.

AdemĂĄs, un filĂłsofo debe coger los dos tenedores adyacentes (es decir, el izquierdo y el derecho) antes de poder comer los espaguetis. El problema del punto muerto se produce cuando los cinco filĂłsofos cogen simultĂĄneamente sus tenedores derechos.

Como cada filĂłsofo tiene un tenedor, todos esperarĂĄn a que los demĂĄs dejen el suyo. Como resultado, ninguno de ellos podrĂĄ comer espaguetis.

De manera similar, en un sistema concurrente, se produce un bloqueo cuando diferentes subprocesos o procesos (filósofos) intentan adquirir los recursos compartidos del sistema (bifurcaciones) al mismo tiempo. Como resultado, ninguno de los procesos tiene la oportunidad de ejecutarse, ya que estån esperando otro recurso que esté en poder de otro proceso.

Condiciones de carrera

Una condiciĂłn de carrera es un estado no deseado de un programa que ocurre cuando un sistema realiza dos o mĂĄs operaciones simultĂĄneamente. Por ejemplo, considere este sencillo bucle for:

i=0; # a global variable
for x in range(100):
    print(i)
    i+=1;

Si creas n Aunque varios hilos ejecutan este código simultåneamente, no es posible determinar el valor de i (que es compartido por los hilos) cuando el programa finaliza su ejecución. Esto se debe a que, en un entorno multihilo real, los hilos pueden superponerse, y el valor de i, que fue recuperado y modificado por un hilo, puede cambiar mientras otro hilo accede a él.

Estas son las dos clases principales de problemas que pueden ocurrir en un sistema multihilo o distribuido. Python aplicaciĂłn. En la siguiente secciĂłn, aprenderĂĄ cĂłmo superar este problema sincronizando los hilos.

Synchilos cronizadores

Para lidiar con condiciones de carrera, bloqueos y otros problemas relacionados con subprocesos, el mĂłdulo de subprocesos proporciona la Bloquear objeto. La idea es que cuando un subproceso desea acceder a un recurso especĂ­fico, adquiere un bloqueo para ese recurso. Una vez que un subproceso bloquea un recurso en particular, ningĂșn otro subproceso puede acceder a Ă©l hasta que se libere el bloqueo. Como resultado, los cambios en el recurso serĂĄn atĂłmicos y se evitarĂĄn las condiciones de carrera.

Un bloqueo es una primitiva de sincronización de bajo nivel implementada por el _hilo módulo. En cualquier momento dado, un bloqueo puede estar en uno de dos estados: cerrado or desbloqueado Admite dos métodos:

  1. adquirir(): Cuando el estado de bloqueo se desbloquea, al llamar al método acquire() se cambia el estado a bloqueado y se devuelve un valor. Sin embargo, si el estado es bloqueado, la llamada a acquire() se bloquea hasta que otro hilo llame al método release().
  2. liberar(): El método release() se utiliza para establecer el estado en desbloqueado, es decir, para liberar un bloqueo. Puede ser llamado por cualquier hilo, no necesariamente por el que adquirió el bloqueo.

AquĂ­ tienes un ejemplo de cĂłmo usar bloqueos en tus aplicaciones. Inicia tu IDLE y escriba lo siguiente:

import threading
lock = threading.Lock()

def first_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the first funcion')
        lock.release()

def second_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the second funcion')
        lock.release()

if __name__=="__main__":
    thread_one = threading.Thread(target=first_function)
    thread_two = threading.Thread(target=second_function)

    thread_one.start()
    thread_two.start()

    thread_one.join()
    thread_two.join()

Ahora presiona F5. DeberĂ­as ver un resultado como este:

SyncHilos de cronizaciĂłn

EXPLICACIÓN DEL CÓDIGO

SyncHilos de cronizaciĂłn

  1. AquĂ­, simplemente estĂĄs creando un nuevo bloqueo llamando al threading.Lock () FunciĂłn de fĂĄbrica. Internamente, Lock() devuelve una instancia de la clase Lock concreta mĂĄs eficaz mantenida por la plataforma.
  2. En la primera declaraciĂłn, adquiere el bloqueo llamando al mĂ©todo adquirir(). Cuando se ha concedido el bloqueo, imprime “bloqueo adquirido” a la consola. Una vez que todo el cĂłdigo que desea que ejecute el subproceso haya finalizado su ejecuciĂłn, libere el bloqueo llamando al mĂ©todo release().

La teoría es correcta, pero ¿cómo saber si el bloqueo funcionó correctamente? Si observas la salida, verås que cada instrucción `print` imprime exactamente una línea a la vez. Recuerda que, en un ejemplo anterior, las salidas de `print` eran aleatorias porque varios hilos accedían al método `print()` simultåneamente. Aquí, la función `print` se llama solo después de adquirir el bloqueo. Por lo tanto, las salidas se muestran una a una y línea por línea.

Aparte de las cerraduras, Python También admite otros mecanismos para gestionar la sincronización de subprocesos, como se indica a continuación:

  1. Bloqueos
  2. Semaphores
  3. Condiciones
  4. Eventos, y
  5. Barreras

Bloqueo global de intérprete (y cómo solucionarlo)

Antes de entrar en los detalles de PythonPara entender mejor la siguiente secciĂłn, definamos algunos tĂ©rminos que serĂĄn Ăștiles:

  1. CĂłdigo que depende de la CPU: esto se refiere a cualquier fragmento de cĂłdigo que serĂĄ ejecutado directamente por la CPU.
  2. Código con restricciones de E/S: puede tratarse de cualquier código que acceda al sistema de archivos a través del sistema operativo.
  3. CPython: es la referencia implementación of Python y puede describirse como el intérprete escrito en C y Python (lenguaje de programación).

¿Qué es GIL en Python?

Bloqueo de intérprete global (GIL) in Python es un bloqueo de proceso o un mutex que se utiliza al tratar con los procesos. Asegura que un hilo pueda acceder a un recurso particular a la vez, y también impide el uso simultåneo de objetos y bytecodes. Esto beneficia a los programas de un solo hilo con un aumento del rendimiento. GIL en Python Es muy sencillo y fåcil de implementar.

Se puede utilizar un bloqueo para garantizar que solo un subproceso tenga acceso a un recurso particular en un momento dado.

Una de las características de Python es que utiliza un bloqueo global en cada proceso intérprete, lo que significa que cada proceso trata el Python el intérprete en sí mismo como recurso.

Por ejemplo, supongamos que has escrito un Python Programa que utiliza dos hilos para realizar operaciones de CPU y de entrada/salida. Al ejecutar este programa, sucede lo siguiente:

  1. El Python El intérprete crea un nuevo proceso y genera los hilos.
  2. Cuando el subproceso 1 comience a ejecutarse, primero adquirirĂĄ el GIL y lo bloquearĂĄ.
  3. Si el subproceso 2 quiere ejecutarse ahora, tendrĂĄ que esperar a que se libere el GIL incluso si hay otro procesador libre.
  4. Ahora, supongamos que el subproceso 1 estĂĄ esperando una operaciĂłn de E/S. En ese momento, liberarĂĄ el GIL y el subproceso 2 lo adquirirĂĄ.
  5. Después de completar las operaciones de E/S, si el subproceso 1 quiere ejecutarse ahora, tendrå que esperar nuevamente a que el subproceso 2 libere el GIL.

Debido a esto, solo un hilo puede acceder al intérprete en cualquier momento, lo que significa que solo habrå un hilo ejecutåndose. Python código en un momento dado.

Esto no supone ningĂșn problema en un procesador de un solo nĂșcleo, ya que se utilizarĂ­a la segmentaciĂłn de tiempo (vĂ©ase la primera secciĂłn de este tutorial) para gestionar los hilos. Sin embargo, en el caso de procesadores multinĂșcleo, una funciĂłn que consume muchos recursos de la CPU y se ejecuta en varios hilos tendrĂĄ un impacto considerable en la eficiencia del programa, puesto que no utilizarĂĄ todos los nĂșcleos disponibles simultĂĄneamente.

¿Por qué se necesitaba GIL?

El CPython El recolector de basura utiliza una técnica eficiente de administración de memoria conocida como conteo de referencias. Así es como funciona: Cada objeto en Python Un objeto tiene un contador de referencias, que aumenta al asignarle un nuevo nombre de variable o al agregarlo a un contenedor (como tuplas, listas, etc.). Del mismo modo, el contador de referencias disminuye cuando la referencia sale del åmbito o cuando se llama a la instrucción `del`. Cuando el contador de referencias de un objeto llega a cero, se elimina mediante el recolector de basura y se libera la memoria asignada.

Pero el problema es que la variable de conteo de referencias es propensa a condiciones de carrera como cualquier otra variable global. Para resolver este problema, los desarrolladores de Python Se decidió utilizar el bloqueo global del intérprete. La otra opción era añadir un bloqueo a cada objeto, lo que habría provocado interbloqueos y un aumento de la sobrecarga derivada de las llamadas a acquire() y release().

Por lo tanto, GIL es una restricciĂłn significativa para el multihilo. Python programas que ejecutan operaciones intensivas que dependen de la CPU (lo que efectivamente los convierte en un solo hilo). Si desea utilizar varios nĂșcleos de CPU en su aplicaciĂłn, utilice el multiprocesamiento mĂłdulo en su lugar.

Preguntas Frecuentes

Utilice subprocesos para tareas que requieren mucha E/S, como llamadas de red, acceso a archivos o consultas a bases de datos, donde los subprocesos esperan recursos externos. Utilice multiprocesamiento para tareas que requieren mucha CPU, como cĂĄlculos complejos, ya que se ejecuta en varios nĂșcleos y evita el GIL (Global Interpreter Lock).

No por defecto. PEP 703 introdujo una compilaciĂłn opcional de subprocesos libres en Python 3.13 que desactiva el GIL y Python La versiĂłn 3.14 continĂșa con esta prĂĄctica. Las compilaciones estĂĄndar aĂșn incluyen el GIL, por lo que la mayorĂ­a de los programas siguen funcionando exactamente igual que antes.

Un hilo demonio se ejecuta en segundo plano y no impide que el programa finalice. Cuando solo quedan hilos demonio, Python Los apaga y sale. Configure uno con thread.daemon = True antes de llamar a start().

La clase Thread no devuelve el resultado de run() directamente. Almacene el valor en el objeto thread o en una cola compartida, o utilice concurrent.futures.ThreadPoolExecutor, cuyo método submit() devuelve un Future que se lee con result().

ThreadPoolExecutor es una funciĂłn auxiliar de alto nivel que gestiona un grupo de subprocesos de trabajo. Se envĂ­an tareas con submit() o map(), y esta funciĂłn se encarga de la creaciĂłn, reutilizaciĂłn y limpieza de los subprocesos, devolviendo objetos Future que contienen cada resultado.

Puedes crear cientos o miles de hilos, pero el GIL solo permite que se ejecute uno. Python cĂłdigo de bytes en cualquier instante. TodavĂ­a se superponen durante las esperas de E/S, por lo que muchos hilos concurrentes ayudan mucho mĂĄs a los programas limitados por E/S que a los limitados por la CPU.

SĂ­, principalmente para pasos que dependen de E/S, como cargar datos, llamar a API o leer archivos. El entrenamiento intensivo depende de la CPU, por lo que se utilizan bibliotecas como NumPy y Py.TorLibere el GIL en el cĂłdigo C o confĂ­e en el multiprocesamiento para lograr un verdadero paralelismo.

SĂ­. GitHub Copilot autocompleta patrones comunes como subclases de Thread, uso de Lock y configuraciones de ThreadPoolExecutor a partir de un comentario o nombre de funciĂłn. Siempre revise el cĂłdigo generado para detectar condiciones de carrera y el manejo correcto de los bloqueos antes de usarlo.

Resumir este post con: