La foo
siguiente función devuelve una cadena 'foo'
. ¿Cómo puedo obtener el valor 'foo'
que se devuelve desde el objetivo del hilo?
from threading import Thread
def foo(bar):
print('hello {}'.format(bar))
return 'foo'
thread = Thread(target=foo, args=('world!',))
thread.start()
return_value = thread.join()
La "forma obvia de hacerlo", que se muestra arriba, no funciona: thread.join()
devuelta None
.
futures = [executor.submit(foo, param) for param in param_list]
El orden se mantendrá y, al salir,with
se permitirá la recopilación de resultados.[f.result() for f in futures]
FWIW, el
multiprocessing
módulo tiene una buena interfaz para esto usando laPool
clase. Y si desea seguir con hilos en lugar de procesos, puede usar lamultiprocessing.pool.ThreadPool
clase como un reemplazo directo.fuente
multiprocess
, no tienen nada que ver con Procesos.processes=1
más de uno si tiene más hilos!Una forma que he visto es pasar un objeto mutable, como una lista o un diccionario, al constructor del hilo, junto con un índice u otro identificador de algún tipo. El hilo puede almacenar sus resultados en su ranura dedicada en ese objeto. Por ejemplo:
Si realmente desea
join()
devolver el valor de retorno de la función llamada, puede hacerlo con unaThread
subclase como la siguiente:Eso se vuelve un poco complicado debido a algunos cambios de nombres, y accede a estructuras de datos "privadas" que son específicas de la
Thread
implementación ... pero funciona.Para python3
fuente
threading
, no una biblioteca diferente para probar, más la limitación del tamaño del grupo presenta un problema potencial adicional, que sucedió en mi caso.TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given
. ¿Alguna manera de arreglar eso?_Thread__target
cosa). Hará que cualquiera que intente portar su código a Python 3 lo odie hasta que resuelva lo que ha hecho (debido al uso de características no documentadas que cambiaron entre 2 y 3). Documente bien su código.La respuesta de Jake es buena, pero si no desea utilizar un conjunto de subprocesos (no sabe cuántos subprocesos necesitará, pero créelos según sea necesario), entonces una buena forma de transmitir información entre subprocesos es el incorporado Clase Queue.Queue , ya que ofrece seguridad de subprocesos.
Creé el siguiente decorador para que actúe de manera similar al conjunto de subprocesos:
Entonces solo lo usas como:
La función decorada crea un nuevo hilo cada vez que se llama y devuelve un objeto Thread que contiene la cola que recibirá el resultado.
ACTUALIZAR
Ha pasado bastante tiempo desde que publiqué esta respuesta, pero aún recibe vistas, así que pensé en actualizarla para reflejar la forma en que lo hago en las versiones más recientes de Python:
Python 3.2 agregado en el
concurrent.futures
módulo que proporciona una interfaz de alto nivel para tareas paralelas. ProporcionaThreadPoolExecutor
yProcessPoolExecutor
, por lo que puede utilizar un subproceso o grupo de procesos con la misma API.Un beneficio de esta API es que enviar una tarea a
Executor
unFuture
objeto objeto, que se completará con el valor de retorno del invocable que envíe.Esto hace que adjuntar un
queue
objeto sea innecesario, lo que simplifica bastante el decorador:Esto usará un módulo predeterminado ejecutor de agrupación de hilos de si no se pasa uno.
El uso es muy similar al anterior:
Si está utilizando Python 3.4+, una característica realmente agradable de usar este método (y los objetos Futuros en general) es que el futuro devuelto se puede ajustar para convertirlo en un
asyncio.Future
conasyncio.wrap_future
. Esto hace que funcione fácilmente con corutinas:Si no necesita acceso al
concurrent.Future
objeto subyacente , puede incluir el ajuste en el decorador:Luego, cada vez que necesite eliminar el código de bloqueo o de uso intensivo de la CPU del hilo del bucle de eventos, puede colocarlo en una función decorada:
fuente
AttributeError: 'module' object has no attribute 'Lock'
parece estar emanando de la líneay = long_task(10)
... ¿pensamientos?Otra solución que no requiere cambiar su código existente:
También se puede ajustar fácilmente a un entorno multiproceso:
fuente
from queue import Queue
.La respuesta
join
/return
respuesta de Parris / kindall portó a Python 3:Tenga en cuenta que la
Thread
clase se implementa de manera diferente en Python 3.fuente
Robé la respuesta de Kindall y la limpié un poco.
La parte clave es agregar * args y ** kwargs a join () para manejar el tiempo de espera
RESPUESTA ACTUALIZADA A CONTINUACIÓN
Esta es mi respuesta popularmente votada, así que decidí actualizar con un código que se ejecutará tanto en py2 como en py3.
Además, veo muchas respuestas a esta pregunta que muestran una falta de comprensión con respecto a Thread.join (). Algunos fallan completamente en manejar el
timeout
argumento. Pero también hay un caso de esquina que debe tener en cuenta con respecto a las instancias en las que tiene (1) una función de destino que puede devolverNone
y (2) también pasa eltimeout
argumento para unirse (). Consulte "PRUEBA 4" para comprender este caso de esquina.Clase ThreadWithReturn que funciona con py2 y py3:
Algunas pruebas de muestra se muestran a continuación:
¿Puedes identificar el caso de esquina que posiblemente podamos encontrar con TEST 4?
El problema es que esperamos que giveMe () devuelva None (ver PRUEBA 2), pero también esperamos que join () devuelva None si se agota el tiempo de espera.
returned is None
significa ya sea:(1) eso es lo que giveMe () devolvió, o
(2) unirse () agotó el tiempo de espera
Este ejemplo es trivial ya que sabemos que giveMe () siempre devolverá None. Pero en el caso del mundo real (donde el objetivo puede devolver legítimamente Ninguno u otra cosa), querríamos verificar explícitamente qué sucedió.
A continuación se muestra cómo abordar este caso de esquina:
fuente
target
,args
ykwargs
los argumentos a init como variables miembro de la clase.Usando la cola:
fuente
out_queue1
necesitará para recorrerout_queue1.get()
y detectar la excepción Queue.Empty:ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass
. Semicolones para simular saltos de línea.Mi solución al problema es envolver la función y el hilo en una clase. No requiere el uso de agrupaciones, colas o paso variable de tipo c. También es no bloqueante. Verifica el estado en su lugar. Vea un ejemplo de cómo usarlo al final del código.
fuente
join
siempre regresoNone
, creo que deberías subclaseThread
para manejar los códigos de retorno y así.fuente
Tomando en consideración @iman comentario sobre @JakeBiesinger respuesta que he recompuesto a tener varias número de hilos:
Salud,
Chico.
fuente
Puede definir una mutable por encima del alcance de la función de subprocesos y agregar el resultado a eso. (También modifiqué el código para que sea compatible con python3)
Esto vuelve
{'world!': 'foo'}
Si usa la entrada de función como la clave para su dictado de resultados, se garantiza que cada entrada única dará una entrada en los resultados
fuente
Estoy usando este contenedor, que convierte cómodamente cualquier función para ejecutar en un
Thread
- cuidando su valor de retorno o excepción. No agregaQueue
gastos generales.Ejemplos de uso
Notas sobre el
threading
móduloEl valor de retorno cómodo y el manejo de excepciones de una función roscada es una necesidad frecuente "Pythonic" y, de hecho, el
threading
módulo ya debería ofrecerlo , posiblemente directamente en laThread
clase estándar .ThreadPool
tiene demasiados gastos generales para tareas simples: 3 subprocesos de gestión, mucha burocracia. LamentablementeThread
, el diseño se copió originalmente de Java, que puede ver, por ejemplo, desde el parámetro del constructor 1st (!), Que sigue siendo inútilgroup
.fuente
Defina su objetivo para
1) tomar un argumento
q
2) reemplazar cualquier declaración
return foo
conq.put(foo); return
entonces una función
se convertiría
y luego procederías como tal
Y puede usar decoradores / envoltorios de funciones para hacerlo, de modo que pueda usar sus funciones existentes
target
sin modificarlas, pero siga este esquema básico.fuente
results = [ans_q.get() for _ in xrange(len(threads))]
Como se mencionó, el grupo de multiprocesamiento es mucho más lento que el subproceso básico. Usar colas como se propone en algunas respuestas aquí es una alternativa muy efectiva. Lo he usado con diccionarios para poder ejecutar muchos hilos pequeños y recuperar múltiples respuestas combinándolas con diccionarios:
fuente
La idea de GuySoft es genial, pero creo que el objeto no necesariamente tiene que heredar de Thread y start () podría eliminarse de la interfaz:
fuente
Una solución habitual es envolver su función
foo
con un decorador comoEntonces todo el código puede verse así
Nota
Una cuestión importante es que los valores de retorno pueden estar desordenados . (De hecho,
return value
no se guarda necesariamente en elqueue
, ya que puede elegir una estructura de datos arbitraria segura para subprocesos )fuente
¿Por qué no solo usar variable global?
fuente
La respuesta de Kindall en Python3
fuente
Si solo se debe validar Verdadero o Falso a partir de la llamada de una función, una solución más simple que encuentro es actualizar una lista global.
Esto es más útil cuando desea encontrar si alguno de los hilos ha devuelto un estado falso para tomar las medidas necesarias.
fuente