Python — Отменить поток таймера

#python

#python

Вопрос:

Я пытаюсь создать метод, который запускается по таймеру в фоновом режиме моего основного скрипта:

 def hello_world(self):
        print 'Hello!'
        threading.Timer(2,hello_world).start()

if __name__ == "__main__":
    try:
        hello_world()
    except KeyboardInterrupt:
        print 'nGoodbye!'
  

Я получаю это сообщение, когда пытаюсь прервать мой скрипт с помощью клавиатуры:

 Exception KeyboardInterrupt in <module 'threading' from '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py'> ignored
  

Как мне закрыть поток, чтобы я мог чисто выйти из своего приложения?

Ответ №1:

Чтобы немного уточнить ответ Aphex, основной поток не может перехватить сигнал KeyboardInterrupt, если у вас нет очень быстрых пальцев. Основной поток завершается почти немедленно! Попробуйте это:

 import threading

def hello_world():
        print 'Hello!'
        threading.Timer(2,hello_world).start()

if __name__ == "__main__":
    try:
        hello_world()
    except KeyboardInterrupt:
        print 'nGoodbye!'
    print "main thread exited"
  

В более общем плане, я бы не советовал использовать подобный таймер самостоятельного вызова только потому, что он создает много потоков. Просто создайте один поток и вызовите time.sleep внутри него.

Однако, пока вы продолжаете работу основного потока, вы, кажется, можете перехватывать KeyboardInterrupt внутри. Тогда хитрость заключается в том, чтобы сделать поток daemon потоком, который завершается при завершении основного потока.

 import threading
import time

def hello_world():
    while(True):
        print 'Hello!'
        time.sleep(2)

if __name__ == "__main__":
    hw_thread = threading.Thread(target = hello_world)
    hw_thread.daemon = True
    hw_thread.start()
    try:
        time.sleep(1000)
    except KeyboardInterrupt:
        print 'nGoodbye!'
  

Это завершается автоматически через 1000 секунд — вы могли бы увеличить это число еще больше, если хотите. Вы также могли бы использовать цикл занятости для повторения вызова режима ожидания, но я действительно не вижу в этом смысла.

Ответ №2:

Вам просто нужно установить Timer поток как daemon

 def hello_world(self):
    print 'Hello!'
    t = threading.Timer(2,hello_world)
    t.daemon = True
    t.start()
  

Это приведет к завершению работы при завершении основного потока, например, из-за KeyboardInterrupt .

daemon Настройка приводит к завершению всей программы, когда остаются только daemon потоки.

Комментарии:

1. Мило! Это правильный ответ. Это единственный способ заставить потоки таймера не допускать, чтобы Ctrl C препятствовали выходу. И, в конечном счете… все таймеры должны включить это… потому что, даже если у вас есть только один… маловероятно, что вы хотите, чтобы Ctrl C отключил хотя бы один таймер …. без завершения основного потока.

Ответ №3:

Попробуйте повторно вызвать KeyboardInterrupt исключение: http://effbot.org/zone/stupid-exceptions-keyboardinterrupt.htm

Это все еще может не сработать; скорее всего, вы сталкиваетесь с этим предостережением:

Потоки странно взаимодействуют с прерываниями: исключение KeyboardInterrupt будет получено произвольным потоком. (Когда модуль signal доступен, прерывания всегда передаются в основной поток.)

Короче говоря, вы не можете быть уверены, что KeyboardInterrupt отправляется в ваш основной поток. Чтобы обойти это, вы можете заглянуть в signal модуль.

Редактировать: Более элегантный способ отменить поток — иметь общую переменную, на которую смотрит поток, и завершает работу, если она становится false . Затем, если вы хотите завершить поток из вашего основного потока, вы устанавливаете переменную в false .

Комментарии:

1. Повторите свою правку: не является общей переменной. Используйте threading.Event для этого