Como obter o valor de retorno de um thread em python?

342

A função fooabaixo retorna uma string 'foo'. Como posso obter o valor 'foo'retornado do destino do thread?

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()

A "maneira óbvia de fazê-lo", mostrada acima, não funciona: thread.join()retornada None.

wim
fonte

Respostas:

37

No Python 3.2+, o concurrent.futuresmódulo stdlib fornece uma API de nível superior threading, incluindo a passagem de valores de retorno ou exceções de um thread de trabalho de volta ao thread principal:

import concurrent.futures

def foo(bar):
    print('hello {}'.format(bar))
    return 'foo'

with concurrent.futures.ThreadPoolExecutor() as executor:
    future = executor.submit(foo, 'world!')
    return_value = future.result()
    print(return_value)
Ramarao Amara
fonte
Para aqueles que se perguntam, isso pode ser feito com uma lista de tópicos. futures = [executor.submit(foo, param) for param in param_list]A ordem será mantida e sair da withpermitirá a coleta de resultados. [f.result() for f in futures]
jayreed1
273

FWIW, o multiprocessingmódulo possui uma interface agradável para isso usando a Poolclasse E se você quiser ficar com threads em vez de processos, pode usar a multiprocessing.pool.ThreadPoolclasse apenas como um substituto.

def foo(bar, baz):
  print 'hello {0}'.format(bar)
  return 'foo' + baz

from multiprocessing.pool import ThreadPool
pool = ThreadPool(processes=1)

async_result = pool.apply_async(foo, ('world', 'foo')) # tuple of args for foo

# do some other stuff in the main process

return_val = async_result.get()  # get the return value from your function.
Jake Biesinger
fonte
50
@JakeBiesinger Meu argumento é que eu estava procurando resposta, como obter resposta do Thread, vim aqui e a resposta aceita não responde à pergunta indicada. Eu diferencio threads e processos. Eu sei sobre o bloqueio global de intérpretes, no entanto, estou trabalhando no problema de ligação de E / S, para que os encadeamentos sejam aceitáveis, não preciso de processos. Outras respostas aqui melhor resposta pergunta declarada.
Omikron 19/04
7
@omikron Mas os threads em python não retornam uma resposta, a menos que você use uma subclasse que habilite essa funcionalidade. Das possíveis subclasses, o ThreadPools é uma ótima opção (escolha o número de threads, use map / apply w / sync / async). Apesar de importados multiprocess, eles não têm nada a ver com processos.
precisa saber é o seguinte
4
@JakeBiesinger Oh, eu sou cego. Desculpe pelos meus comentários desnecessários. Você está certo. Eu apenas assumi que multiprocessamento = processos.
Omikron
12
Não se esqueça de definir processes=1mais de um, se você tiver mais tópicos!
Iman
4
O problema com o multiprocessamento e o conjunto de encadeamentos é que é muito mais lento configurar e iniciar encadeamentos em comparação com a biblioteca de encadeamento básica. É ótimo para iniciar threads de execução longa, mas derrota o objetivo quando é necessário iniciar uma série de threads de execução curta. A solução de usar "threading" e "Queue" documentada em outras respostas aqui é uma alternativa melhor para esse último caso de uso na minha opinião.
Yves Dorfsman
242

Uma maneira que eu vi é passar um objeto mutável, como uma lista ou um dicionário, para o construtor do thread, junto com um índice ou outro identificador de algum tipo. O thread pode armazenar seus resultados em seu slot dedicado nesse objeto. Por exemplo:

def foo(bar, result, index):
    print 'hello {0}'.format(bar)
    result[index] = "foo"

from threading import Thread

threads = [None] * 10
results = [None] * 10

for i in range(len(threads)):
    threads[i] = Thread(target=foo, args=('world!', results, i))
    threads[i].start()

# do some other stuff

for i in range(len(threads)):
    threads[i].join()

print " ".join(results)  # what sound does a metasyntactic locomotive make?

Se você realmente deseja join()retornar o valor de retorno da função chamada, você pode fazer isso com uma Threadsubclasse como a seguinte:

from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return "foo"

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, Verbose=None):
        Thread.__init__(self, group, target, name, args, kwargs, Verbose)
        self._return = None
    def run(self):
        if self._Thread__target is not None:
            self._return = self._Thread__target(*self._Thread__args,
                                                **self._Thread__kwargs)
    def join(self):
        Thread.join(self)
        return self._return

twrv = ThreadWithReturnValue(target=foo, args=('world!',))

twrv.start()
print twrv.join()   # prints foo

Isso fica um pouco cabeludo por causa de algum nome incorreto e acessa estruturas de dados "particulares" que são específicas da Threadimplementação ... mas funciona.

Para python3

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, Verbose=None):
        Thread.__init__(self, group, target, name, args, kwargs)
        self._return = None
    def run(self):
        print(type(self._target))
        if self._target is not None:
            self._return = self._target(*self._args,
                                                **self._kwargs)
    def join(self, *args):
        Thread.join(self, *args)
        return self._return
kindall
fonte
37
legal, obrigado pelo exemplo! Eu me pergunto por que o Thread não foi implementado com a manipulação de um valor de retorno em primeiro lugar, parece uma coisa óbvia o suficiente para suportar.
Wim
16
Eu acho que essa deve ser a resposta aceita - o OP pediu threading, não uma biblioteca diferente para tentar, além da limitação do tamanho do pool, introduz um problema potencial adicional, o que aconteceu no meu caso.
domoarigato 31/01
10
Grande piada de trem.
meawoppl
7
No python3, isso retorna TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given. Alguma maneira de consertar isso?
30516 GuySoft
2
Aviso para alguém tentado a fazer o segundo deles (a _Thread__targetcoisa). Você fará com que qualquer pessoa que tente portar seu código no python 3 o odeie até descobrir o que você fez (por usar recursos não documentados que foram alterados entre 2 e 3). Documente bem seu código.
Ben Taylor
84

A resposta de Jake é boa, mas se você não quiser usar um pool de threads (você não sabe quantos threads precisará, mas os cria conforme necessário), uma boa maneira de transmitir informações entre os threads é o built-in Queue.Queue Classe , pois oferece segurança de encadeamento.

Criei o seguinte decorador para fazê-lo agir de maneira semelhante ao pool de threads:

def threaded(f, daemon=False):
    import Queue

    def wrapped_f(q, *args, **kwargs):
        '''this function calls the decorated function and puts the 
        result in a queue'''
        ret = f(*args, **kwargs)
        q.put(ret)

    def wrap(*args, **kwargs):
        '''this is the function returned from the decorator. It fires off
        wrapped_f in a new thread and returns the thread object with
        the result queue attached'''

        q = Queue.Queue()

        t = threading.Thread(target=wrapped_f, args=(q,)+args, kwargs=kwargs)
        t.daemon = daemon
        t.start()
        t.result_queue = q        
        return t

    return wrap

Então você apenas o usa como:

@threaded
def long_task(x):
    import time
    x = x + 5
    time.sleep(5)
    return x

# does not block, returns Thread object
y = long_task(10)
print y

# this blocks, waiting for the result
result = y.result_queue.get()
print result

A função decorada cria um novo thread toda vez que é chamada e retorna um objeto Thread que contém a fila que receberá o resultado.

ATUALIZAR

Já faz um bom tempo desde que publiquei esta resposta, mas ela ainda tem visualizações, então pensei em atualizá-la para refletir a maneira como faço isso nas versões mais recentes do Python:

Python 3.2 adicionado no concurrent.futuresmódulo que fornece uma interface de alto nível para tarefas paralelas. Fornece ThreadPoolExecutoreProcessPoolExecutor , portanto, você pode usar um encadeamento ou pool de processos com a mesma API.

Um benefício dessa API é que o envio de uma tarefa a um Executorretorno aFuture objeto , que será concluído com o valor de retorno da chamada que você enviar.

Isso torna queuedesnecessário anexar um objeto, o que simplifica bastante o decorador:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return (executor or _DEFAULT_POOL).submit(f, *args, **kwargs)

    return wrap

Isso usará um módulo padrão executor de pool de threads do se um não for passado.

O uso é muito semelhante ao anterior:

@threadpool
def long_task(x):
    import time
    x = x + 5
    time.sleep(5)
    return x

# does not block, returns Future object
y = long_task(10)
print y

# this blocks, waiting for the result
result = y.result()
print result

Se você estiver usando Python 3.4+, uma característica realmente agradável de usar este método (e Futuro objetos em geral) é que o futuro retornado pode ser embrulhado para transformá-lo em um asyncio.Futurecom asyncio.wrap_future. Isso facilita o trabalho com corotinas:

result = await asyncio.wrap_future(long_task(10))

Se você não precisar acessar o concurrent.Futureobjeto subjacente , poderá incluir o wrap no decorador:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return asyncio.wrap_future((executor or _DEFAULT_POOL).submit(f, *args, **kwargs))

    return wrap

Então, sempre que você precisar enviar código intensivo da CPU ou bloquear o segmento do loop de eventos, poderá colocá-lo em uma função decorada:

@threadpool
def some_long_calculation():
    ...

# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()
bj0
fonte
Parece que não consigo fazer isso funcionar; Recebo um erro informando que AttributeError: 'module' object has no attribute 'Lock'isso parece emanar da linha y = long_task(10)... pensamentos?
sadmicrowave
11
O código não usa explicitamente o bloqueio, portanto, o problema pode estar em outro lugar no seu código. Você pode postar uma nova pergunta sobre o SO
bj0
Por que result_queue é um atributo de instância? Seria melhor se fosse um atributo de classe para que os usuários não precisem chamar result_queue ao usar @threaded, o que não é explícito e ambíguo?
nonbot
@ t88, não sei o que você quer dizer, você precisa de alguma maneira de acessar o resultado, o que significa que você precisa saber o que chamar. Se você quiser que seja outra coisa, pode subclassificar o Thread e fazer o que quiser (essa foi uma solução simples). A razão pela qual a fila precisa ser conectada ao encadeamento é para que várias chamadas / funções tenham suas próprias filas
bj0
11
Isto é brilhante! Muito obrigado.
Ganesh Kathiresan
53

Outra solução que não requer alteração do código existente:

import Queue
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return 'foo'

que = Queue.Queue()

t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
t.join()
result = que.get()
print result

Também pode ser facilmente ajustado para um ambiente multiencadeado:

import Queue
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return 'foo'

que = Queue.Queue()
threads_list = list()

t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
threads_list.append(t)

# Add more threads here
...
threads_list.append(t2)
...
threads_list.append(t3)
...

# Join all the threads
for t in threads_list:
    t.join()

# Check thread's return value
while not que.empty():
    result = que.get()
    print result
Arik
fonte
t = Thread (target = lambda q, arg1: q.put (foo (arg1)), args = (que, 'world!')) o que o q.put está fazendo aqui, o que faz o Queue.Queue ()
vijay shanker 29/10
6
Deve haver uma estátua sua em sua cidade natal, obrigado!
Onilol 18/04
3
@ Onilol - Muito obrigado. O seu comentário é exatamente a razão de eu fazer isso :)
Arik
4
Para Python3, é necessário mudar para from queue import Queue.
Gino Mempin 6/02/19
11
Esse parece ser o método menos perturbador (não há necessidade de reestruturar dramaticamente a base de código original) para permitir que o valor de retorno retorne ao thread principal.
Fanchen Bao
24

Resposta join / returnresposta de Parris / kindall portada para Python 3:

from threading import Thread

def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon=daemon)

        self._return = None

    def run(self):
        if self._target is not None:
            self._return = self._target(*self._args, **self._kwargs)

    def join(self):
        Thread.join(self)
        return self._return


twrv = ThreadWithReturnValue(target=foo, args=('world!',))

twrv.start()
print(twrv.join())   # prints foo

Observe que a Threadclasse é implementada de maneira diferente no Python 3.

GuySoft
fonte
11
join requer um parâmetro de tempo limite que deve ser passado
adiante
22

Roubei a resposta de kindall e a limpei um pouco.

A parte principal é adicionar * args e ** kwargs a join () para lidar com o tempo limite

class threadWithReturn(Thread):
    def __init__(self, *args, **kwargs):
        super(threadWithReturn, self).__init__(*args, **kwargs)

        self._return = None

    def run(self):
        if self._Thread__target is not None:
            self._return = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)

    def join(self, *args, **kwargs):
        super(threadWithReturn, self).join(*args, **kwargs)

        return self._return

RESPOSTA ATUALIZADA ABAIXO

Esta é a minha resposta mais votada popularmente, por isso decidi atualizar com o código que será executado nos py2 e py3.

Além disso, vejo muitas respostas a essa pergunta que mostram uma falta de compreensão sobre o Thread.join (). Alguns falham completamente ao lidar com o timeoutargumento. Mas há também um caso em que você deve estar ciente das instâncias em que possui (1) uma função de destino que pode retornar Nonee (2) você também passa otimeout argumento para entrar em (). Consulte "TESTE 4" para entender este caso de canto.

Classe ThreadWithReturn que funciona com py2 e py3:

import sys
from threading import Thread
from builtins import super    # https://stackoverflow.com/a/30159479

if sys.version_info >= (3, 0):
    _thread_target_key = '_target'
    _thread_args_key = '_args'
    _thread_kwargs_key = '_kwargs'
else:
    _thread_target_key = '_Thread__target'
    _thread_args_key = '_Thread__args'
    _thread_kwargs_key = '_Thread__kwargs'

class ThreadWithReturn(Thread):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._return = None

    def run(self):
        target = getattr(self, _thread_target_key)
        if not target is None:
            self._return = target(
                *getattr(self, _thread_args_key),
                **getattr(self, _thread_kwargs_key)
            )

    def join(self, *args, **kwargs):
        super().join(*args, **kwargs)
        return self._return

Alguns exemplos de testes são mostrados abaixo:

import time, random

# TEST TARGET FUNCTION
def giveMe(arg, seconds=None):
    if not seconds is None:
        time.sleep(seconds)
    return arg

# TEST 1
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',))
my_thread.start()
returned = my_thread.join()
# (returned == 'stringy')

# TEST 2
my_thread = ThreadWithReturn(target=giveMe, args=(None,))
my_thread.start()
returned = my_thread.join()
# (returned is None)

# TEST 3
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=2)
# (returned is None) # because join() timed out before giveMe() finished

# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))

Você consegue identificar a caixa de canto que podemos encontrar com o TESTE 4?

O problema é que esperamos que o giveMe () retorne None (consulte TEST 2), mas também esperamos que o join () retorne None se o tempo limite exceder.

returned is None significa:

(1) foi isso que giveMe () retornou, ou

(2) tempo limite de participação () expirado

Este exemplo é trivial, pois sabemos que giveMe () sempre retornará None. Mas, no caso do mundo real (onde o destino pode legitimamente retornar Nenhum ou outra coisa), gostaríamos de verificar explicitamente o que aconteceu.

Abaixo está como lidar com este caso de canto:

# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))

if my_thread.isAlive():
    # returned is None because join() timed out
    # this also means that giveMe() is still running in the background
    pass
    # handle this based on your app's logic
else:
    # join() is finished, and so is giveMe()
    # BUT we could also be in a race condition, so we need to update returned, just in case
    returned = my_thread.join()
user2426679
fonte
Você conhece o equivalente _Thread_target para Python3? Esse atributo não existe no Python3.
GreySage
Eu olhei no arquivo threading.py, verifica-se que é _target (outros atributos têm o mesmo nome).
GreySage
Você pode evitar o acesso as variáveis particulares da classe Thread, se você salvar os target, argse kwargsargumentos para o init como variáveis de membro em sua classe.
Tolli
@GreySage Veja a minha resposta, eu portado este bloco para python3 abaixo
GuySoft
A resposta do @GreySage agora é compatível com py2 e py3
user2426679
15

Usando fila:

import threading, queue

def calc_square(num, out_queue1):
  l = []
  for x in num:
    l.append(x*x)
  out_queue1.put(l)


arr = [1,2,3,4,5,6,7,8,9,10]
out_queue1=queue.Queue()
t1=threading.Thread(target=calc_square, args=(arr,out_queue1))
t1.start()
t1.join()
print (out_queue1.get())
user341143
fonte
11
Realmente gosto dessa solução, curta e doce. Se a sua função lê uma fila de entrada, e você adicionar à out_queue1você terá de varrer out_queue1.get()e capturar a exceção Queue.Empty: ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass. Ponto e vírgula para simular quebras de linha.
sastorsl
6

Minha solução para o problema é agrupar a função e o thread em uma classe. Não requer o uso de conjuntos, filas ou passagem de variável do tipo c. Também não é bloqueador. Você verifica o status. Veja o exemplo de como usá-lo no final do código.

import threading

class ThreadWorker():
    '''
    The basic idea is given a function create an object.
    The object can then run the function in a thread.
    It provides a wrapper to start it,check its status,and get data out the function.
    '''
    def __init__(self,func):
        self.thread = None
        self.data = None
        self.func = self.save_data(func)

    def save_data(self,func):
        '''modify function to save its returned data'''
        def new_func(*args, **kwargs):
            self.data=func(*args, **kwargs)

        return new_func

    def start(self,params):
        self.data = None
        if self.thread is not None:
            if self.thread.isAlive():
                return 'running' #could raise exception here

        #unless thread exists and is alive start or restart it
        self.thread = threading.Thread(target=self.func,args=params)
        self.thread.start()
        return 'started'

    def status(self):
        if self.thread is None:
            return 'not_started'
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return 'finished'

    def get_results(self):
        if self.thread is None:
            return 'not_started' #could return exception
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return self.data

def add(x,y):
    return x +y

add_worker = ThreadWorker(add)
print add_worker.start((1,2,))
print add_worker.status()
print add_worker.get_results()
Peter Lonjers
fonte
como você lidaria com uma exceção? digamos que a função add foi dada e int e str. todos os threads falhariam ou apenas um falharia?
user1745713
4

joinretornar sempre None, acho que você deve subclasse Threadpara lidar com códigos de retorno e assim.

Chuva de ideias
fonte
4

Levando em consideração @iman comentário sobre @JakeBiesinger resposta que recomposta que ele tem várias número de tópicos:

from multiprocessing.pool import ThreadPool

def foo(bar, baz):
    print 'hello {0}'.format(bar)
    return 'foo' + baz

numOfThreads = 3 
results = []

pool = ThreadPool(numOfThreads)

for i in range(0, numOfThreads):
    results.append(pool.apply_async(foo, ('world', 'foo'))) # tuple of args for foo)

# do some other stuff in the main process
# ...
# ...

results = [r.get() for r in results]
print results

pool.close()
pool.join()

Felicidades,

Cara.

Guy Avraham
fonte
2

Você pode definir um mutável acima do escopo da função encadeada e adicionar o resultado a isso. (Também modifiquei o código para ser compatível com python3)

returns = {}
def foo(bar):
    print('hello {0}'.format(bar))
    returns[bar] = 'foo'

from threading import Thread
t = Thread(target=foo, args=('world!',))
t.start()
t.join()
print(returns)

Isso retorna {'world!': 'foo'}

Se você usar a entrada de função como a chave para seu ditado de resultados, é garantida uma entrada única para fornecer uma entrada nos resultados

Thijs D
fonte
2

Estou usando esse invólucro, que confortavelmente ativa qualquer função para executar em Thread- cuidando de seu valor de retorno ou exceção. Não adiciona Queuesobrecarga.

def threading_func(f):
    """Decorator for running a function in a thread and handling its return
    value or exception"""
    def start(*args, **kw):
        def run():
            try:
                th.ret = f(*args, **kw)
            except:
                th.exc = sys.exc_info()
        def get(timeout=None):
            th.join(timeout)
            if th.exc:
                raise th.exc[0], th.exc[1], th.exc[2] # py2
                ##raise th.exc[1] #py3                
            return th.ret
        th = threading.Thread(None, run)
        th.exc = None
        th.get = get
        th.start()
        return th
    return start

Exemplos de uso

def f(x):
    return 2.5 * x
th = threading_func(f)(4)
print("still running?:", th.is_alive())
print("result:", th.get(timeout=1.0))

@threading_func
def th_mul(a, b):
    return a * b
th = th_mul("text", 2.5)

try:
    print(th.get())
except TypeError:
    print("exception thrown ok.")

Notas sobre o threadingmódulo

O valor de retorno confortável e o tratamento de exceção de uma função encadeada são uma necessidade "pitônica" frequente e, de fato, já devem ser oferecidos pelo threadingmódulo - possivelmente diretamente na Threadclasse padrão . ThreadPoolpossui muita sobrecarga para tarefas simples - 3 gerenciamento de threads, muita burocracia. Infelizmente Thread, o layout foi copiado do Java originalmente - o que você vê, por exemplo, do ainda inútil parâmetro do construtor 1st (!) group.

kxr
fonte
o primeiro construtor não é inútil, a sua reservados lá para futura implementação .. a partir de livro de receitas de programação Python paralelo
Vijay Shanker
1

Defina seu alvo para
1) adote um argumento q
2) substitua quaisquer declarações return fooporq.put(foo); return

então uma função

def func(a):
    ans = a * a
    return ans

se tornaria

def func(a, q):
    ans = a * a
    q.put(ans)
    return

e então você continuaria como tal

from Queue import Queue
from threading import Thread

ans_q = Queue()
arg_tups = [(i, ans_q) for i in xrange(10)]

threads = [Thread(target=func, args=arg_tup) for arg_tup in arg_tups]
_ = [t.start() for t in threads]
_ = [t.join() for t in threads]
results = [q.get() for _ in xrange(len(threads))]

E você pode usar decoradores / wrappers de funções para fazê-lo, para poder usar suas funções existentes targetsem modificá-las, mas siga este esquema básico.

tscizzle
fonte
Deveria serresults = [ans_q.get() for _ in xrange(len(threads))]
Hemant H Kumar
1

Como mencionado, o pool de multiprocessamento é muito mais lento que o encadeamento básico. Usar filas como proposto em algumas respostas aqui é uma alternativa muito eficaz. Eu uso-o com dicionários para poder executar muitos threads pequenos e recuperar várias respostas combinando-os com dicionários:

#!/usr/bin/env python3

import threading
# use Queue for python2
import queue
import random

LETTERS = 'abcdefghijklmnopqrstuvwxyz'
LETTERS = [ x for x in LETTERS ]

NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

def randoms(k, q):
    result = dict()
    result['letter'] = random.choice(LETTERS)
    result['number'] = random.choice(NUMBERS)
    q.put({k: result})

threads = list()
q = queue.Queue()
results = dict()

for name in ('alpha', 'oscar', 'yankee',):
    threads.append( threading.Thread(target=randoms, args=(name, q)) )
    threads[-1].start()
_ = [ t.join() for t in threads ]
while not q.empty():
    results.update(q.get())

print(results)
Yves Dorfsman
fonte
1

A idéia da GuySoft é ótima, mas acho que o objeto não precisa necessariamente herdar do Thread e o start () pode ser removido da interface:

from threading import Thread
import queue
class ThreadWithReturnValue(object):
    def __init__(self, target=None, args=(), **kwargs):
        self._que = queue.Queue()
        self._t = Thread(target=lambda q,arg1,kwargs1: q.put(target(*arg1, **kwargs1)) ,
                args=(self._que, args, kwargs), )
        self._t.start()

    def join(self):
        self._t.join()
        return self._que.get()


def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

twrv = ThreadWithReturnValue(target=foo, args=('world!',))

print(twrv.join())   # prints foo
pandy.song
fonte
0

Uma solução usual é envolver sua função foocom um decorador como

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

Então o código inteiro pode ser assim

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list]

for t in threads:
    t.start()
    while(True):
        if(len(threading.enumerate()) < max_num):
            break
for t in threads:
    t.join()
return result

Nota

Uma questão importante é que os valores de retorno podem ser desordenados . (Na verdade, o arquivo return valuenão é necessariamente salvo no queue, pois você pode escolher uma estrutura de dados arbitrária e segura para threads )

Response777
fonte
0

Por que não usar apenas variável global?

import threading


class myThread(threading.Thread):
    def __init__(self, ind, lock):
        threading.Thread.__init__(self)
        self.ind = ind
        self.lock = lock

    def run(self):
        global results
        with self.lock:
            results.append(self.ind)



results = []
lock = threading.Lock()
threads = [myThread(x, lock) for x in range(1, 4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(results)
Alexey
fonte
0

Resposta de Kindall em Python3

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon)
        self._return = None 

    def run(self):
        try:
            if self._target:
                self._return = self._target(*self._args, **self._kwargs)
        finally:
            del self._target, self._args, self._kwargs 

    def join(self,timeout=None):
        Thread.join(self,timeout)
        return self._return
SmartManoj
fonte
-2

Se apenas True ou False for validado a partir de uma chamada de função, uma solução mais simples que eu acho é atualizar uma lista global.

import threading

lists = {"A":"True", "B":"True"}

def myfunc(name: str, mylist):
    for i in mylist:
        if i == 31:
            lists[name] = "False"
            return False
        else:
            print("name {} : {}".format(name, i))

t1 = threading.Thread(target=myfunc, args=("A", [1, 2, 3, 4, 5, 6], ))
t2 = threading.Thread(target=myfunc, args=("B", [11, 21, 31, 41, 51, 61], ))
t1.start()
t2.start()
t1.join()
t2.join()

for value in lists.values():
    if value == False:
        # Something is suspicious 
        # Take necessary action 

Isso é mais útil quando você deseja descobrir se algum dos threads retornou um status falso para executar a ação necessária.

Sravya
fonte