Eu sou muito novo em Python e programação multithread em geral. Basicamente, eu tenho um script que copiará arquivos para outro local. Gostaria que isso fosse colocado em outro encadeamento, para que eu possa ....
mostrar que o script ainda está em execução.
O problema que estou enfrentando é que, se os arquivos não puderem ser copiados, haverá uma exceção. Isso está ok se estiver sendo executado no thread principal; no entanto, ter o seguinte código não funciona:
try:
threadClass = TheThread(param1, param2, etc.)
threadClass.start() ##### **Exception takes place here**
except:
print "Caught an exception"
Na própria classe do thread, tentei relançar a exceção, mas ela não funciona. Vi pessoas aqui fazendo perguntas semelhantes, mas todas parecem estar fazendo algo mais específico do que estou tentando fazer (e não entendo bem as soluções oferecidas). Vi pessoas mencionarem o uso sys.exc_info()
, mas não sei onde nem como usá-lo.
Toda ajuda é bem-vinda!
EDIT: O código para a classe de thread está abaixo:
class TheThread(threading.Thread):
def __init__(self, sourceFolder, destFolder):
threading.Thread.__init__(self)
self.sourceFolder = sourceFolder
self.destFolder = destFolder
def run(self):
try:
shul.copytree(self.sourceFolder, self.destFolder)
except:
raise
TheThread
? Exemplo de código, talvez?Respostas:
O problema é que
thread_obj.start()
retorna imediatamente. O thread filho que você gerou é executado em seu próprio contexto, com sua própria pilha. Qualquer exceção que ocorra existe no contexto do encadeamento filho e está em sua própria pilha. Uma maneira de pensar agora para comunicar essas informações ao encadeamento pai é usar algum tipo de passagem de mensagem, para que você possa analisar isso.Tente isso para obter o tamanho:
fonte
multiprocessing
equivalente: gist.github.com/2311116bucket.get()
aumentoQueue.Empty
? Em seguida, o threadjoin(0.1)
será concluído eisAlive() is False
, e você perderá sua exceção.Queue
é desnecessário neste caso simples - você pode simplesmente armazenar as informações da exceção como uma propriedadeExcThread
, desde que sejarun()
concluído logo após a exceção (o que ocorre neste exemplo simples). Então você simplesmente aumenta novamente a exceção depois (ou durante)t.join()
. Não há problemas de sincronização porquejoin()
garante que o encadeamento tenha sido concluído. Veja a resposta de Rok Strniša abaixo stackoverflow.com/a/12223550/126362O
concurrent.futures
módulo simplifica o trabalho em threads (ou processos) separados e manipula todas as exceções resultantes:concurrent.futures
está incluído no Python 3.2 e está disponível como módulo de backportfutures
para versões anteriores.fonte
concurrent.futures.as_completed
, você pode ser imediatamente notificado quando exceções forem geradas: stackoverflow.com/questions/2829329/…Existem muitas respostas realmente estranhamente complicadas para essa pergunta. Estou simplificando demais isso, porque isso parece suficiente para a maioria das coisas para mim.
Se você tiver certeza de que estará rodando apenas em uma ou outra versão do Python, poderá reduzir o
run()
método para apenas a versão desconectada (se estiver rodando apenas nas versões do Python antes da 3) ou apenas a versão limpa (se você só estiver executando versões do Python começando com 3).Exemplo de uso:
E você verá a exceção levantada no outro segmento quando ingressar.
Se você estiver usando
six
ou apenas no Python 3, poderá melhorar as informações de rastreamento de pilha que obtém quando a exceção é gerada novamente. Em vez de apenas a pilha no ponto da junção, você pode agrupar a exceção interna em uma nova exceção externa e obter os dois rastreamentos de pilha comou
fonte
Embora não seja possível capturar diretamente uma exceção lançada em um encadeamento diferente, aqui está um código para obter de maneira bastante transparente algo muito próximo a essa funcionalidade. Seu segmento filho deve subclassificar a
ExThread
classe em vez dethreading.Thread
e o segmento pai deve chamar ochild_thread.join_with_exception()
método em vez dechild_thread.join()
aguardar o término do trabalho.Detalhes técnicos desta implementação: quando o encadeamento filho lança uma exceção, é passado ao pai através de um
Queue
e lançado novamente no encadeamento pai. Observe que não há espera ocupada nessa abordagem.fonte
BaseException
, nãoException
? Tudo o que você está fazendo é propagar a exceção de umaThread
para outra. No momento, o IE, aKeyboardInterrupt
, seria silenciosamente ignorado se fosse gerado em um encadeamento em segundo plano.join_with_exception
trava indefinidamente se chamado pela segunda vez em um segmento morto. Correção: github.com/fraserharris/threading-extensions/blob/master/…Queue
necessário; veja meu comentário na resposta do @ Santa. Você pode simplificá-lo para algo como a resposta de Rok Strniša abaixo stackoverflow.com/a/12223550/126362Se ocorrer uma exceção em um encadeamento, a melhor maneira é aumentá-lo novamente durante o encadeamento do chamador
join
. Você pode obter informações sobre a exceção atualmente sendo tratada usando asys.exc_info()
função Essas informações podem simplesmente ser armazenadas como uma propriedade do objeto de encadeamento atéjoin
serem chamadas e, nesse ponto, podem ser aumentadas novamente.Observe que a
Queue.Queue
(como sugerido em outras respostas) não é necessário neste caso simples em que o thread lança no máximo 1 exceção e é concluído logo após o lançamento de uma exceção . Evitamos as condições da corrida, simplesmente aguardando a conclusão do segmento.Por exemplo, estenda
ExcThread
(abaixo), substituindoexcRun
(em vez derun
).Python 2.x:
Python 3.x:
O formulário de 3 argumentos
raise
foi removido no Python 3, então altere a última linha para:fonte
concurrent.futures.as_completed
https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed
A seguinte solução:
Queue
Fonte:
Saída possível:
Infelizmente, não é possível matar futuros para cancelar os outros porque um falha:
concurrent.features
; Python: concurrent.futures Como torná-lo cancelável?threading
: Existe alguma maneira de matar um thread?Se você fizer algo como:
então o
with
pega e aguarda a conclusão do segundo encadeamento antes de continuar. O seguinte se comporta de maneira semelhante:já que
future.result()
gera novamente a exceção, se houver uma.Se você deseja sair de todo o processo Python, pode se safar
os._exit(0)
, mas isso provavelmente significa que você precisa de um refator.Classe personalizada com perfeita semântica de exceção
Acabei codificando a interface perfeita para mim em: O caminho certo para limitar o número máximo de threads em execução ao mesmo tempo? seção "Exemplo de fila com tratamento de erros". Essa classe tem como objetivo ser conveniente e fornecer controle total sobre o envio e a manipulação de resultados / erros.
Testado em Python 3.6.7, Ubuntu 18.04.
fonte
Esse era um pequeno problema desagradável, e eu gostaria de lançar minha solução. Algumas outras soluções que encontrei (async.io por exemplo) pareciam promissoras, mas também apresentavam um pouco de caixa preta. A abordagem de fila / loop de eventos meio que o vincula a uma determinada implementação. O código-fonte de futuros concorrentes, no entanto, é de apenas 1000 linhas e fácil de compreender . Ele me permitiu resolver facilmente meu problema: criar threads de trabalho ad-hoc sem muita configuração e conseguir capturar exceções no thread principal.
Minha solução usa a API de futuros simultâneos e a API de segmentação. Ele permite que você crie um trabalhador que fornece o thread e o futuro. Dessa forma, você pode ingressar no segmento para aguardar o resultado:
... ou você pode permitir que o funcionário envie um retorno de chamada quando terminar:
... ou você pode fazer um loop até que o evento seja concluído:
Aqui está o código:
... e a função de teste:
fonte
Como noobie do Threading, demorei muito tempo para entender como implementar o código de Mateusz Kobos (acima). Aqui está uma versão esclarecida para ajudar a entender como usá-lo.
fonte
De maneira semelhante à de RickardSjogren sem Queue, sys etc., mas também sem alguns ouvintes para sinais: execute diretamente um manipulador de exceção que corresponda a um bloco de exceção.
Somente self._callback e o bloco de exceção em run () são adicionais ao threading.Thread normal.
fonte
Eu sei que estou um pouco atrasado para a festa aqui, mas estava tendo um problema muito semelhante, mas incluía o uso do tkinter como uma GUI, e o mainloop tornou impossível o uso de qualquer uma das soluções que dependem de .join (). Portanto, adaptei a solução dada no EDIT da pergunta original, mas a tornei mais geral para facilitar o entendimento para os outros.
Aqui está a nova classe de encadeamento em ação:
É claro que você sempre pode lidar com a exceção de alguma outra maneira, desde o log, como imprimi-la ou enviá-la para o console.
Isso permite que você use a classe ExceptionThread exatamente como faria na classe Thread, sem nenhuma modificação especial.
fonte
Um método de que gosto é baseado no padrão de observador . Defino uma classe de sinal que meu segmento usa para emitir exceções para os ouvintes. Também pode ser usado para retornar valores de threads. Exemplo:
Não tenho experiência suficiente em trabalhar com threads para afirmar que esse é um método completamente seguro. Mas funcionou para mim e eu gosto da flexibilidade.
fonte
Usar excertos nus não é uma boa prática, porque geralmente você pega mais do que espera.
Eu sugeriria que modificasse o
except
para pegar APENAS a exceção que você gostaria de tratar. Não acho que aumentá-lo tenha o efeito desejado, porque quando você vai instanciarTheThread
no exteriortry
, se isso gera uma exceção, a tarefa nunca vai acontecer.Em vez disso, você pode apenas alertá-lo e seguir em frente, como:
Então, quando essa exceção for detectada, você poderá lidar com ela lá. Então, quando o externo
try
capturar uma exceçãoTheThread
, você sabe que não será o que você já tratou e ajudará a isolar o fluxo do processo.fonte
Uma maneira simples de capturar a exceção do encadeamento e se comunicar com o método de chamada pode ser passando o dicionário ou uma lista para o
worker
método.Exemplo (passando o dicionário para o método de trabalho):
fonte
Enrole o Thread com armazenamento de exceção.
fonte
O pygolang fornece sync.WorkGroup que, em particular, propaga exceção dos threads de trabalho gerados para o thread principal. Por exemplo:
fornece o seguinte quando executado:
O código original da pergunta seria apenas:
fonte