La foosiguiente 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,withse permitirá la recopilación de resultados.[f.result() for f in futures]FWIW, el
multiprocessingmódulo tiene una buena interfaz para esto usando laPoolclase. Y si desea seguir con hilos en lugar de procesos, puede usar lamultiprocessing.pool.ThreadPoolclase como un reemplazo directo.fuente
multiprocess, no tienen nada que ver con Procesos.processes=1má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 unaThreadsubclase 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
Threadimplementació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__targetcosa). 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.futuresmódulo que proporciona una interfaz de alto nivel para tareas paralelas. ProporcionaThreadPoolExecutoryProcessPoolExecutor, 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
ExecutorunFutureobjeto objeto, que se completará con el valor de retorno del invocable que envíe.Esto hace que adjuntar un
queueobjeto 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.Futureconasyncio.wrap_future. Esto hace que funcione fácilmente con corutinas:Si no necesita acceso al
concurrent.Futureobjeto 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/returnrespuesta de Parris / kindall portó a Python 3:Tenga en cuenta que la
Threadclase 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
timeoutargumento. 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 devolverNoney (2) también pasa eltimeoutargumento 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 Nonesignifica 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,argsykwargslos argumentos a init como variables miembro de la clase.Usando la cola:
fuente
out_queue1necesitará 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
joinsiempre regresoNone, creo que deberías subclaseThreadpara 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 agregaQueuegastos generales.Ejemplos de uso
Notas sobre el
threadingmó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
threadingmódulo ya debería ofrecerlo , posiblemente directamente en laThreadclase estándar .ThreadPooltiene 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
q2) reemplazar cualquier declaración
return fooconq.put(foo); returnentonces 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
targetsin 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
foocon 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 valueno 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