A função foo
abaixo 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
.
futures = [executor.submit(foo, param) for param in param_list]
A ordem será mantida e sair dawith
permitirá a coleta de resultados.[f.result() for f in futures]
FWIW, o
multiprocessing
módulo possui uma interface agradável para isso usando aPool
classe E se você quiser ficar com threads em vez de processos, pode usar amultiprocessing.pool.ThreadPool
classe apenas como um substituto.fonte
multiprocess
, eles não têm nada a ver com processos.processes=1
mais de um, se você tiver mais tópicos!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:
Se você realmente deseja
join()
retornar o valor de retorno da função chamada, você pode fazer isso com umaThread
subclasse como a seguinte:Isso fica um pouco cabeludo por causa de algum nome incorreto e acessa estruturas de dados "particulares" que são específicas da
Thread
implementação ... mas funciona.Para python3
fonte
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.TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given
. Alguma maneira de consertar isso?_Thread__target
coisa). 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.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:
Então você apenas o usa como:
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.futures
módulo que fornece uma interface de alto nível para tarefas paralelas. ForneceThreadPoolExecutor
eProcessPoolExecutor
, 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
Executor
retorno aFuture
objeto , que será concluído com o valor de retorno da chamada que você enviar.Isso torna
queue
desnecessário anexar um objeto, o que simplifica bastante o decorador: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:
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.Future
comasyncio.wrap_future
. Isso facilita o trabalho com corotinas:Se você não precisar acessar o
concurrent.Future
objeto subjacente , poderá incluir o wrap no decorador: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:
fonte
AttributeError: 'module' object has no attribute 'Lock'
isso parece emanar da linhay = long_task(10)
... pensamentos?Outra solução que não requer alteração do código existente:
Também pode ser facilmente ajustado para um ambiente multiencadeado:
fonte
from queue import Queue
.Resposta
join
/return
resposta de Parris / kindall portada para Python 3:Observe que a
Thread
classe é implementada de maneira diferente no Python 3.fonte
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
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
timeout
argumento. 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 retornarNone
e (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:
Alguns exemplos de testes são mostrados abaixo:
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:
fonte
target
,args
ekwargs
argumentos para o init como variáveis de membro em sua classe.Usando fila:
fonte
out_queue1
você terá de varrerout_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.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.
fonte
join
retornar sempreNone
, acho que você deve subclasseThread
para lidar com códigos de retorno e assim.fonte
Levando em consideração @iman comentário sobre @JakeBiesinger resposta que recomposta que ele tem várias número de tópicos:
Felicidades,
Cara.
fonte
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)
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
fonte
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 adicionaQueue
sobrecarga.Exemplos de uso
Notas sobre o
threading
móduloO 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
threading
módulo - possivelmente diretamente naThread
classe padrão .ThreadPool
possui muita sobrecarga para tarefas simples - 3 gerenciamento de threads, muita burocracia. InfelizmenteThread
, o layout foi copiado do Java originalmente - o que você vê, por exemplo, do ainda inútil parâmetro do construtor 1st (!)group
.fonte
Defina seu alvo para
1) adote um argumento
q
2) substitua quaisquer declarações
return foo
porq.put(foo); return
então uma função
se tornaria
e então você continuaria como tal
E você pode usar decoradores / wrappers de funções para fazê-lo, para poder usar suas funções existentes
target
sem modificá-las, mas siga este esquema básico.fonte
results = [ans_q.get() for _ in xrange(len(threads))]
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:
fonte
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:
fonte
Uma solução usual é envolver sua função
foo
com um decorador comoEntão o código inteiro pode ser assim
Nota
Uma questão importante é que os valores de retorno podem ser desordenados . (Na verdade, o arquivo
return value
não é necessariamente salvo noqueue
, pois você pode escolher uma estrutura de dados arbitrária e segura para threads )fonte
Por que não usar apenas variável global?
fonte
Resposta de Kindall em Python3
fonte
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.
Isso é mais útil quando você deseja descobrir se algum dos threads retornou um status falso para executar a ação necessária.
fonte