É possível finalizar um thread em execução sem configurar / verificar nenhum sinalizador / semáforo / etc.?
fonte
É possível finalizar um thread em execução sem configurar / verificar nenhum sinalizador / semáforo / etc.?
Geralmente, é um padrão ruim matar um thread abruptamente, em Python e em qualquer idioma. Pense nos seguintes casos:
A boa maneira de lidar com isso, se você puder pagar (se estiver gerenciando seus próprios encadeamentos), é ter um sinalizador exit_request que cada encadeamento verifica em intervalos regulares para ver se é hora de sair.
Por exemplo:
import threading
class StoppableThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self, *args, **kwargs):
super(StoppableThread, self).__init__(*args, **kwargs)
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
Nesse código, você deve chamar stop()
o encadeamento quando quiser que ele saia e aguardar o encadeamento sair corretamente usando join()
. A linha deve verificar o sinalizador de parada em intervalos regulares.
No entanto, existem casos em que você realmente precisa matar um thread. Um exemplo é quando você está agrupando uma biblioteca externa ocupada para chamadas longas e deseja interrompê-la.
O código a seguir permite (com algumas restrições) gerar uma exceção em um thread Python:
def _async_raise(tid, exctype):
'''Raises an exception in the threads with id tid'''
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# "if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
raise SystemError("PyThreadState_SetAsyncExc failed")
class ThreadWithExc(threading.Thread):
'''A thread class that supports raising exception in the thread from
another thread.
'''
def _get_my_tid(self):
"""determines this (self's) thread id
CAREFUL : this function is executed in the context of the caller
thread, to get the identity of the thread represented by this
instance.
"""
if not self.isAlive():
raise threading.ThreadError("the thread is not active")
# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id
# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid
# TODO: in python 2.6, there's a simpler way to do : self.ident
raise AssertionError("could not determine the thread's id")
def raiseExc(self, exctype):
"""Raises the given exception type in the context of this thread.
If the thread is busy in a system call (time.sleep(),
socket.accept(), ...), the exception is simply ignored.
If you are sure that your exception should terminate the thread,
one way to ensure that it works is:
t = ThreadWithExc( ... )
...
t.raiseExc( SomeException )
while t.isAlive():
time.sleep( 0.1 )
t.raiseExc( SomeException )
If the exception is to be caught by the thread, you need a way to
check that your thread has caught it.
CAREFUL : this function is executed in the context of the
caller thread, to raise an excpetion in the context of the
thread represented by this instance.
"""
_async_raise( self._get_my_tid(), exctype )
(Baseado em tópicos matáveis de Tomer Filiba. A citação sobre o valor de retorno de PyThreadState_SetAsyncExc
parece ser de uma versão antiga do Python .)
Conforme observado na documentação, isso não é um item mágico, porque se o encadeamento estiver ocupado fora do interpretador Python, ele não detectará a interrupção.
Um bom padrão de uso desse código é fazer com que o encadeamento capture uma exceção específica e execute a limpeza. Dessa forma, você pode interromper uma tarefa e ainda ter uma limpeza adequada.
SO_REUSEADDR
opção de soquete para evitarAddress already in use
erros.None
em vez de0
para ores != 1
caso, e eu tive que chamarctypes.c_long(tid)
e passar isso para qualquer ctypes funcionar em vez do tid diretamente.Não existe uma API oficial para fazer isso, não.
Você precisa usar a API da plataforma para eliminar o encadeamento, por exemplo, pthread_kill ou TerminateThread. Você pode acessar essa API, por exemplo, através de pythonwin ou ctypes.
Observe que isso é inerentemente inseguro. Provavelmente, isso resultará em lixo incobrável (das variáveis locais dos quadros de pilha que se tornam lixo) e poderá levar a conflitos, se o encadeamento que está sendo morto tiver o GIL no ponto em que é morto.
fonte
Uma
multiprocessing.Process
latap.terminate()
Nos casos em que quero matar um encadeamento, mas não quero usar sinalizadores / bloqueios / sinais / semáforos / eventos / o que for, eu promovo os encadeamentos para processos completos. Para código que utiliza apenas alguns threads, a sobrecarga não é tão ruim assim.
Por exemplo, isso é útil para encerrar facilmente "threads" auxiliares que executam E / S de bloqueio
A conversão é trivial: no código relacionado, substitua all
threading.Thread
pormultiprocessing.Process
e allqueue.Queue
withmultiprocessing.Queue
e adicione as chamadas necessárias dep.terminate()
seu processo pai, que deseja matar seu filho.p
Veja a documentação
multiprocessing
do Python para .fonte
multiprocessing
é bom, mas lembre-se de que os argumentos são selecionados para o novo processo. Portanto, se um dos argumentos for algo não selecionável (como alogging.log
), pode não ser uma boa ideia usarmultiprocessing
.multiprocessing
argumentos são selecionados para o novo processo no Windows, mas o Linux usa bifurcação para copiá-los (Python 3.7, sem saber quais outras versões). Então, você terminará com um código que funciona no Linux, mas gera erros de pickle no Windows.multiprocessing
com o registro é um negócio complicado. Precisa usarQueueHandler
(consulte este tutorial ). Eu aprendi da maneira mais difícil.Se você está tentando finalizar o programa inteiro, pode definir o encadeamento como um "daemon". consulte Thread.daemon
fonte
Como outros já mencionaram, a norma é definir um sinalizador de parada. Para algo leve (sem subclassificação de Thread, sem variável global), um retorno de chamada lambda é uma opção. (Observe os parênteses em
if stop()
.)Substituir
print()
por umapr()
função que sempre libera (sys.stdout.flush()
) pode melhorar a precisão da saída do shell.(Testado apenas no Windows / Eclipse / Python3.3)
fonte
pr()
função?Isso é baseado no thread2 - threads matáveis (receita Python)
Você precisa chamar PyThreadState_SetasyncExc (), que está disponível apenas através de ctypes.
Isso foi testado apenas no Python 2.7.3, mas é provável que funcione com outras versões 2.x recentes.
fonte
KeyboardInterrupt
uma chance de limpeza. Se eles ainda estão suspensos depois disso,SystemExit
é apropriado ou simplesmente interrompa o processo a partir de um terminal.pthread_cleanup_push()/_pop()
, seria muito trabalhoso implementar corretamente e isso tornaria o interpretador mais lento.Você nunca deve matar um fio à força sem cooperar com ele.
Matar um encadeamento remove todas as garantias que os blocos try / finalmente configuram para que você possa deixar bloqueios bloqueados, arquivos abertos etc.
O único momento em que você pode argumentar que matar forçosamente threads é uma boa ideia é matar um programa rapidamente, mas nunca threads únicos.
fonte
No Python, você simplesmente não pode matar um Thread diretamente.
Se você NÃO precisa realmente de um Thread (!), O que você pode fazer, em vez de usar o pacote de threading , é usar o pacote de multiprocessamento . Aqui, para matar um processo, você pode simplesmente chamar o método:
Python matará seu processo (no Unix, através do sinal SIGTERM, enquanto no Windows, através da
TerminateProcess()
chamada). Preste atenção para usá-lo enquanto estiver usando uma fila ou um cano! (pode corromper os dados na fila / canal)Observe que o
multiprocessing.Event
e omultiprocessing.Semaphore
trabalho funcionam exatamente da mesma maneira que othreading.Event
e othreading.Semaphore
respectivamente. De fato, os primeiros são clones dos últimos.Se você REALMENTE precisar usar um Thread, não há como matá-lo diretamente. O que você pode fazer, no entanto, é usar um "thread daemon" . De fato, em Python, um Thread pode ser sinalizado como daemon :
O programa principal será encerrado quando nenhum encadeamento ativo não-daemon for deixado. Em outras palavras, quando seu encadeamento principal (que é, obviamente, um encadeamento que não é daemon) termina suas operações, o programa será encerrado mesmo se ainda houver alguns encadeamentos daemon funcionando.
Observe que é necessário definir um Thread como
daemon
antes dostart()
método ser chamado!Claro que você pode e deve usar
daemon
mesmo commultiprocessing
. Aqui, quando o processo principal termina, ele tenta finalizar todos os seus processos filhos daemônicos.Finalmente, por favor, note que
sys.exit()
eos.kill()
não são escolhas.fonte
Você pode eliminar um encadeamento instalando o rastreio no encadeamento que sairá do encadeamento. Veja o link em anexo para uma possível implementação.
Mate um tópico em Python
fonte
Se você está chamando explicitamente
time.sleep()
como parte de seu segmento (polling digamos algum serviço externo), uma melhoria sobre o método de Phillipe é usar o tempo limite naevent
'swait()
método onde quer que vocêsleep()
Por exemplo:
Então, para executá-lo
A vantagem de usar em
wait()
vez desleep()
ing e verificar regularmente o evento é que você pode programar em intervalos mais longos de suspensão, o encadeamento é interrompido quase imediatamente (quando você estariasleep()
ining) e, na minha opinião, o código para lidar com a saída é significativamente mais simples .fonte
É melhor se você não matar um fio. Uma maneira seria introduzir um bloco "try" no ciclo do encadeamento e lançar uma exceção quando você quiser interromper o encadeamento (por exemplo, uma quebra / retorno / ... que interrompa seu processo por / enquanto / ...). Eu usei isso no meu aplicativo e funciona ...
fonte
Definitivamente, é possível implementar um
Thread.stop
método conforme mostrado no seguinte código de exemplo:A
Thread3
classe parece executar código aproximadamente 33% mais rápido que aThread2
classe.fonte
self.__stop
conjunto. Observe que, como a maioria das outras soluções aqui, ele realmente não interrompe uma chamada de bloqueio, pois a função de rastreamento só é chamada quando um novo escopo local é inserido. Também digno de nota é o quesys.settrace
realmente significa implementar implementadores de depuradores, perfis etc. e, como tal, é considerado um detalhe de implementação do CPython, e não é garantido que exista em outras implementações do Python.Thread2
classe é que ela executa código aproximadamente dez vezes mais lento. Algumas pessoas ainda podem achar isso aceitável.Esse é o seu
Thread
objeto.Leia a fonte python (
Modules/threadmodule.c
ePython/thread_pthread.h
) você pode ver queThread.ident
é umpthread_t
tipo, para que você possa fazer o quepthread
puder no uso de pythonlibpthread
.fonte
A solução alternativa a seguir pode ser usada para eliminar um encadeamento:
Isso pode ser usado mesmo para encerrar threads, cujo código está escrito em outro módulo, a partir do thread principal. Podemos declarar uma variável global nesse módulo e usá-la para finalizar threads gerados nesse módulo.
Eu costumo usar isso para finalizar todos os threads na saída do programa. Essa pode não ser a maneira perfeita de encerrar os threads, mas pode ajudar.
fonte
Estou muito atrasado para este jogo, mas tenho lutado com uma pergunta semelhante e o seguinte parece resolver o problema perfeitamente para mim E me permite fazer uma verificação e limpeza básicas do estado do encadeamento quando o sub-encadeamento daemonizado terminar:
Rendimentos:
fonte
SystemExit
oafter_timeout
encadeamento faria alguma coisa com o encadeamento principal (que está simplesmente aguardando o primeiro sair neste exemplo)?SystemExit
possui apenas duas propriedades especiais: não produz um retorno (quando um thread sai ao lançar um) e, se o thread principal sai ao lançar um, define o status de saída (enquanto ainda espera por outros threads que não sejam daemon sair).Uma coisa que quero acrescentar é que, se você ler a documentação oficial no threading lib Python , é recomendável evitar o uso de threads "demoníacos", quando você não quiser que os threads terminem abruptamente, com a bandeira que Paolo Rovelli mencionou .
Da documentação oficial:
Penso que a criação de threads daemônicos depende da sua aplicação, mas em geral (e na minha opinião) é melhor evitar matá-los ou torná-los daemônicos. No multiprocessamento, você pode usar
is_alive()
para verificar o status do processo e "encerrar" para finalizá-los (também evita problemas de GIL). Mas, às vezes, você pode encontrar mais problemas ao executar seu código no Windows.E lembre-se sempre de que, se você tiver "threads ativos", o interpretador Python estará executando para aguardá-los. (Devido a esse daemônico, você pode ajudá-lo se não importa abruptamente).
fonte
Existe uma biblioteca criada para esse fim, stopit . Embora algumas das mesmas precauções listadas aqui ainda se apliquem, pelo menos essa biblioteca apresenta uma técnica regular e repetível para atingir o objetivo declarado.
fonte
Embora seja bastante antiga, essa pode ser uma solução útil para alguns:
Portanto, ele permite que um "encadeamento crie exceções no contexto de outro encadeamento" e, dessa maneira, o encadeamento finalizado pode lidar com o encerramento sem verificar regularmente um sinalizador de cancelamento.
No entanto, de acordo com sua fonte original , há alguns problemas com esse código.
fonte
Isso parece funcionar com o pywin32 no windows 7
fonte
Pieter Hintjens - um dos fundadores do projeto ØMQ - diz que usar o ØMQ e evitar primitivas de sincronização como bloqueios, mutexes, eventos etc. é a maneira mais segura e segura de escrever programas multithread:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
Isso inclui dizer a um thread filho que ele deve cancelar seu trabalho. Isso seria feito equipando o thread com um soquete ØMQ e pesquisando nesse soquete para obter uma mensagem dizendo que ele deveria ser cancelado.
O link também fornece um exemplo no código python multiencadeado com ØMQ.
fonte
Assumindo que você deseja ter vários threads da mesma função, esta é IMHO a implementação mais fácil de parar um por id:
O legal é que aqui você pode ter várias funções iguais e diferentes e interromper todas elas
functionname.stop
Se você deseja ter apenas um segmento da função, não precisa se lembrar do id. Apenas pare, se
doit.stop
> 0.fonte
Apenas para desenvolver a idéia do @ SCB (que era exatamente o que eu precisava) para criar uma subclasse KillableThread com uma função personalizada:
Naturalmente, como no @SBC, o thread não espera para executar um novo loop para parar. Neste exemplo, você veria a mensagem "Killing Thread" impressa logo após a mensagem "About to kill thread", em vez de esperar mais 4 segundos para que o thread fosse concluído (já que dormimos por 6 segundos).
O segundo argumento no construtor KillableThread é sua função personalizada (print_msg aqui). O argumento Args são os argumentos que serão usados ao chamar a função (("hello world")) aqui.
fonte
Como mencionado na resposta de @ Kozyarchuk , a instalação de rastreamento funciona. Como esta resposta não continha código, eis um exemplo pronto para uso:
Para depois de imprimir
1
e2
.3
não é impresso.fonte
Você pode executar seu comando em um processo e depois matá-lo usando a identificação do processo. Eu precisava sincronizar entre dois threads, um dos quais não retorna por si só.
fonte
Inicie o subencadeamento com setDaemon (True).
fonte
Veja como fazê-lo:
Aguarde alguns segundos para que seu tópico seja interrompido. Verifique também o
thread._Thread__delete()
método.Eu recomendaria um
thread.quit()
método por conveniência. Por exemplo, se você tiver um soquete em seu encadeamento, recomendo criar umquit()
método em sua classe de identificador de soquete, encerrar o soquete e executar umthread._Thread__stop()
interior do seuquit()
.fonte
_Thread__stop()
apenas marca um segmento como parado , na verdade não para o segmento! Nunca faça isso. Tenha uma leitura .Se você realmente precisa eliminar uma subtarefa, use uma implementação alternativa.
multiprocessing
egevent
ambos apóiam indiscriminadamente matar um "fio".A segmentação do Python não suporta cancelamento. Nem tente. É muito provável que seu código entre em conflito, corrompa ou vaze memória, ou tenha outros efeitos "interessantes" difíceis de depurar não intencionais que ocorrem raramente e sem determinação.
fonte