Невозможно убить скрипт Python с помощью Ctrl-C

117

Я тестирую потоки Python с помощью следующего скрипта:

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()

Это работает в Python 2.7 на Kubuntu 11.10. Ctrl+ Cне убьет. Я также пробовал добавить обработчик системных сигналов, но это не помогло:

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

Чтобы убить процесс, я убиваю его с помощью PID после отправки программы в фоновый режим с помощью Ctrl+ Z, который не игнорируется. Почему Ctrl+ Cигнорируются так настойчиво? Как я могу это решить?

dotancohen
источник
@dotancohen это работает в Windows?
kiriloff 06
@vitaibian: я не тестировал в Windows, но, похоже, это не зависит от ОС.
dotancohen

Ответы:

178

Ctrl+ Cзавершает основной поток, но поскольку ваши потоки не в режиме демона, они продолжают работать, и это поддерживает процесс. Мы можем сделать их демонами:

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

Но есть еще одна проблема: как только основной поток запустил ваши потоки, ему больше нечего делать. Итак, он завершается, и потоки уничтожаются мгновенно. Итак, оставим основной поток живым:

import time
while True:
    time.sleep(1)

Теперь он будет печатать «первый» и «второй», пока вы не нажмете Ctrl+ C.

Изменить: как указывали комментаторы, потоки демона могут не получить возможности очистить такие вещи, как временные файлы. Если вам это нужно, то поймайте KeyboardInterruptосновной поток и попросите его координировать очистку и завершение работы. Но во многих случаях, вероятно, достаточно позволить потокам демонов внезапно умереть.

Томас К
источник
6
вы должны упомянуть, что при этом потоки не останавливаются корректно и некоторые ресурсы не освобождаются.
Tommaso Barbugli
1
Что ж, Ctrl-C - никогда не изящный способ что-либо остановить. Я не уверен, какие ресурсы останутся - разве ОС не должна что-либо восстанавливать при выходе из процесса?
Thomas K
7
@ThomasK Временные файлы, созданные, tempfile.TemporaryFile()например, можно оставить на диске.
Feuermurmel
1
@ deed02392: я точно не знаю, что происходит с основным потоком, но насколько я знаю, вы не можете ничего с ним сделать после его выхода. Процесс завершится, когда завершатся все потоки, не являющиеся демонами; Отношения между родителями и детьми сюда не входят.
Thomas K
4
Похоже, в python3 вы можете перейтиdaemon=True кThread.__init__
Райан Хейнинг
3

Я думаю, что лучше вызывать join () для ваших потоков, когда вы ожидаете, что они умрут. Я взял на себя некоторую вольность с вашим кодом, чтобы цикл закончился (вы также можете добавить туда все, что потребуется для очистки). Переменная die проверяется на истинность при каждом проходе, и когда она истинна, программа завершается.

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глупо, вы должны joinпрямо - и эта функция переопределения вызывает сомнения. Может def join(self, force=False): if force: self.die = Trueтак что join()без изменений join(force=True)убивает их. Но даже в этом случае лучше сообщить обоим потокам, прежде чем присоединяться к любому из них.
o11c
0

Улучшенная версия ответа @Thomas K:

  • Определение вспомогательной функции в is_any_thread_alive()соответствии с этой сутью , которая может main()автоматически завершать работу .

Примеры кодов:

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)
Hansimov
источник