Não é possível matar o script Python com Ctrl-C


117

Estou testando o threading Python com o seguinte script:

import threading

class FirstThread (threading.Thread):
    def run (self):
        while True:
            print 'first'

class SecondThread (threading.Thread):
    def run (self):
        while True:
            print 'second'

FirstThread().start()
SecondThread().start()

Ele está sendo executado no Python 2.7 no Kubuntu 11.10. Ctrl+ Cnão vai matá-lo. Também tentei adicionar um manipulador para sinais do sistema, mas isso não ajudou:

import signal 
import sys
def signal_handler(signal, frame):
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)

Para matar o processo estou eliminando-o por PID após enviar o programa para segundo plano com Ctrl+ Z, que não está sendo ignorado. Por que Ctrl+ está Csendo ignorado tão persistentemente? Como posso resolver isto?


@dotancohen está funcionando no Windows?
Kiriloff de

@vitaibian: Não testei no Windows, mas parece não ser específico do sistema operacional.
dotancohen

Respostas:


178

Ctrl+ Ctermina o thread principal, mas como seus threads não estão no modo daemon, eles continuam em execução, e isso mantém o processo ativo. Podemos torná-los daemons:

f = FirstThread()
f.daemon = True
f.start()
s = SecondThread()
s.daemon = True
s.start()

Mas então há outro problema - uma vez que o thread principal iniciou seus threads, não há mais nada para fazer. Então, ele sai e os fios são destruídos instantaneamente. Então, vamos manter o tópico principal vivo:

import time
while True:
    time.sleep(1)

Agora, ele manterá a impressão 'primeiro' e 'segundo' até que você pressione Ctrl+ C.

Edit: como comentadores apontaram, os threads do daemon podem não ter a chance de limpar coisas como arquivos temporários. Se você precisar disso, pegue o KeyboardInterruptno thread principal e faça com que ele coordene a limpeza e o desligamento. Mas, em muitos casos, permitir que os threads do daemon morram repentinamente é provavelmente o suficiente.


6
você deve mencionar que, ao fazer isso, os threads não são interrompidos normalmente e alguns recursos não são liberados.
Tommaso Barbugli

1
Bem, Ctrl-C nunca é uma maneira elegante de parar qualquer coisa. Não tenho certeza de quais recursos sobrariam - o sistema operacional não deveria reivindicar nada quando o processo for encerrado?
Thomas K de

7
@ThomasK Arquivos temporários criados por tempfile.TemporaryFile()podem ser deixados no disco, por exemplo.
Feuermurmel

1
@ deed02392: Não sei exatamente o que acontece com o thread principal, mas, pelo que sei, você não pode fazer nada com ele depois de encerrado. O processo terminará quando todos os encadeamentos não-daemon tiverem terminado; relacionamentos pai-filho não entram nisso.
Thomas K

4
Parece que em python3 você pode passardaemon=True paraThread.__init__
Ryan Haining


3

Eu acho que é melhor chamar join () em seus threads quando você espera que eles morram. Tomei alguma liberdade com o seu código para fazer os loops terminar (você também pode adicionar qualquer necessidade de limpeza necessária). O dado variável é verificado quanto à verdade em cada passagem e, quando for True, o programa é encerrado.

import threading
import time

class MyThread (threading.Thread):
    die = False
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name

    def run (self):
        while not self.die:
            time.sleep(1)
            print (self.name)

    def join(self):
        self.die = True
        super().join()

if __name__ == '__main__':
    f = MyThread('first')
    f.start()
    s = MyThread('second')
    s.start()
    try:
        while True:
            time.sleep(2)
    except KeyboardInterrupt:
        f.join()
        s.join()

while Trueé bobo, você deveria joindiretamente - e essa função substituída é um tanto questionável. Talvez def join(self, force=False): if force: self.die = Trueisso join()seja inalterado por join(force=True)mata-los. Mas mesmo assim, é melhor informar os dois tópicos antes de entrar em qualquer um deles.
11c

0

Uma versão melhorada da resposta de @Thomas K:

  • Definir uma função de assistente de is_any_thread_alive()acordo com essa essência , que pode encerrar o main()automaticamente.

Códigos de exemplo:

import threading

def job1():
    ...

def job2():
    ...

def is_any_thread_alive(threads):
    return True in [t.is_alive() for t in threads]

if __name__ == "__main__":
    ...
    t1 = threading.Thread(target=job1,daemon=True)
    t2 = threading.Thread(target=job2,daemon=True)
    t1.start()
    t2.start()

    while is_any_thread_alive([t1,t2]):
        time.sleep(0)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.