Como posso lidar com eventos KeyboardInterrupt com os pools de multiprocessamento do python? Aqui está um exemplo simples:
from multiprocessing import Pool
from time import sleep
from sys import exit
def slowly_square(i):
sleep(1)
return i*i
def go():
pool = Pool(8)
try:
results = pool.map(slowly_square, range(40))
except KeyboardInterrupt:
# **** THIS PART NEVER EXECUTES. ****
pool.terminate()
print "You cancelled the program!"
sys.exit(1)
print "\nFinally, here are the results: ", results
if __name__ == "__main__":
go()
Ao executar o código acima, ele KeyboardInterrupt
é gerado quando pressiono ^C
, mas o processo simplesmente trava nesse momento e eu tenho que eliminá-lo externamente.
Quero poder pressionar ^C
a qualquer momento e fazer com que todos os processos sejam encerrados normalmente.
python
multiprocessing
pool
keyboardinterrupt
Fragsworth
fonte
fonte
Respostas:
Este é um bug do Python. Ao aguardar uma condição em threading.Condition.wait (), KeyboardInterrupt nunca é enviado. Repro:
A exceção KeyboardInterrupt não será entregue até wait () retornar e nunca retornar, portanto a interrupção nunca acontece. KeyboardInterrupt quase certamente deve interromper uma espera de condição.
Observe que isso não acontece se um tempo limite for especificado; cond.wait (1) receberá a interrupção imediatamente. Portanto, uma solução alternativa é especificar um tempo limite. Para fazer isso, substitua
com
ou similar.
fonte
Pelo que descobri recentemente, a melhor solução é configurar os processos de trabalho para ignorar completamente o SIGINT e restringir todo o código de limpeza ao processo pai. Isso corrige o problema para os processos de trabalho ocioso e ocupado e não requer nenhum código de manipulação de erros nos processos filhos.
A explicação e o código de exemplo completo podem ser encontrados em http://noswap.com/blog/python-multiprocessing-keyboardinterrupt/ e http://github.com/jreese/multiprocessing-keyboardinterrupt, respectivamente.
fonte
time.sleep(10)
processo principal. Se você remover esse sono, ou se você esperar que o processo tente ingressar na piscina, o que você deve fazer para garantir que os trabalhos sejam concluídos, você ainda sofrerá com o mesmo problema que o processo principal não faz. recebe o KeyboardInterrupt enquanto espera em umajoin
operação de votação .pool.terminate()
nunca são executados. Ter as crianças ignorando o sinal não realiza nada. A resposta de @ Glenn resolve o problema..join()
exceto na interrupção - simplesmente verifica manualmente o resultado do.apply_async()
usoAsyncResult.ready()
para ver se está pronto, o que significa que terminamos corretamente.Por alguns motivos, apenas exceções herdadas da
Exception
classe base são tratadas normalmente. Como solução alternativa, você pode aumentar novamente o seuKeyboardInterrupt
como umaException
instância:Normalmente você obteria a seguinte saída:
Então, se você acertar
^C
, receberá:fonte
KeyboardInterrupt
chegar enquantomultiprocessing
estiver realizando sua própria troca de dados IPC,try..catch
ele não será ativado (obviamente).raise KeyboardInterruptError
por umreturn
. Você só precisa garantir que o processo filho termine assim que o KeyboardInterrupt for recebido. O valor de retorno parece ser ignorado,main
ainda o KeyboardInterrupt é recebido.Geralmente essa estrutura simples funciona para Ctrl- Cno Pool:
Como foi afirmado em alguns posts semelhantes:
Capture a interrupção do teclado no Python sem tentar, exceto
fonte
A resposta votada não aborda a questão central, mas um efeito colateral semelhante.
Jesse Noller, o autor da biblioteca de multiprocessamento, explica como lidar corretamente com CTRL + C ao usar
multiprocessing.Pool
em uma postagem de blog antiga .fonte
os.setpgrp()
de dentro do futuroProcessPoolExecutor
não suporta as funções do inicializador. No Unix, você pode alavancar afork
estratégia desativando o manipulador de sinais no processo principal antes de criar o pool e reativá-lo posteriormente. No seixo , silencioSIGINT
nos processos filho por padrão. Não estou ciente do motivo pelo qual eles não fazem o mesmo com os Python Pools. No final, o usuário pode redefinir oSIGINT
manipulador caso ele / ela queira se machucar.Parece que há dois problemas que fazem exceções, enquanto o multiprocessamento é irritante. A primeira (observada por Glenn) é que você precisa usar
map_async
com um tempo limite em vez demap
obter uma resposta imediata (ou seja, não termine de processar a lista inteira). O segundo (observado por Andrey) é que o multiprocessamento não captura exceções que não são herdadas deException
(por exemplo,SystemExit
). Então, aqui está minha solução que lida com os dois:fonte
function
é bastante longa (centenas de segundos).map
e tudo estará bem.@Linux Cli Aik
forneceu uma solução abaixo que produz esse comportamento. O usomap_async
nem sempre é desejado se o thread principal depender dos resultados dos processos filho.Descobri, por enquanto, a melhor solução é não usar o recurso multiprocessing.pool, mas sim rolar sua própria funcionalidade de pool. Forneci um exemplo demonstrando o erro com apply_async, bem como um exemplo mostrando como evitar o uso completo da funcionalidade do pool.
http://www.bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/
fonte
Eu sou um novato em Python. Eu estava procurando em todos os lugares por respostas e encontrei este e alguns outros blogs e vídeos do youtube. Eu tentei copiar, colar o código do autor acima e reproduzi-lo no meu python 2.7.13 no windows 7 de 64 bits. É perto do que eu quero alcançar.
Fiz meus processos filhos ignorar o ControlC e fazer com que o processo pai fosse encerrado. Parece que ignorar o processo filho evita esse problema para mim.
A parte iniciada em
pool.terminate()
nunca parece ser executada.fonte
map_async
o usuário, o que eu particularmente não gosto. Em muitas situações, como a minha, o thread principal precisa aguardar a conclusão dos processos individuais. Esta é uma das razões pelas quaismap
existe!Você pode tentar usar o método apply_async de um objeto Pool, assim:
Resultado:
Uma vantagem desse método é que os resultados processados antes da interrupção serão retornados no dicionário de resultados:
fonte
Estranhamente, parece que você precisa lidar
KeyboardInterrupt
com as crianças também. Eu esperava que isso funcionasse como está escrito ... tente mudarslowly_square
para:Isso deve funcionar como você esperava.
fonte