Неможливо вбити сценарій 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 це працює в Windows?
kiriloff

@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
Ви повинні зазначити, що виконуючи ці теми, вони не зупиняються витончено і деякі ресурси не вивільняються
Томмазо Барбуглі

1
Що ж, Ctrl-C ніколи не є витонченим способом нічого зупинити. Я не впевнений, які ресурси залишатимуться - чи не повинна ОС повертати щось, коли процес закінчується?
Томас К

7
@ThomasK Наприклад, створені тимчасові файли tempfile.TemporaryFile()можуть залишатися на диску.
Feuermurmel

1
@ deed02392: Я не знаю точно, що відбувається з основною темою, але, наскільки я знаю, ви нічого не можете з цим зробити після її завершення. Процес закінчиться, коли всі недемонові нитки закінчені; стосунки батько-дитина не вступають у це.
Томас К

4
Схоже, в python3 ви можете перейтиdaemon=True доThread.__init__
Ryan Haining


3

Я думаю, що найкраще зателефонувати join () на свої теми, коли ви очікуєте, що вони помруть. Я взяв трохи свободи з вашим кодом, щоб закінчити цикли (ви можете додати туди, що потрібно для очищення, і там). Змінна матриця перевіряється на істинність при кожному проході, і коли це правда, тоді програма виходить.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.