Python - 取消计时器线程

5 投票
3 回答
8994 浏览
提问于 2025-04-16 17:22

我正在尝试创建一个方法,让它在我的主脚本后台定时运行:

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

我该怎么做才能关闭这个线程,以便我可以干净利落地退出我的应用程序呢?

3 个回答

2

试着重新抛出 KeyboardInterrupt 异常:http://effbot.org/zone/stupid-exceptions-keyboardinterrupt.htm

不过,这可能还是不管用;你很可能遇到了这个问题

线程和中断的互动很奇怪:KeyboardInterrupt 异常会被随机的线程接收到。(当信号模块可用时,中断总是会发送到主线程。)

简单来说,你不能确定 KeyboardInterrupt 会发送到你的主线程。为了解决这个问题,你可以看看signal模块。

编辑:一种更优雅的取消线程的方法是使用一个共享变量,线程会查看这个变量,如果它变成 false,线程就会退出。然后,如果你想从主线程中结束这个线程,你只需把这个变量设置为 false

11

你只需要把 Timer 线程设置为 daemon 线程。

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

这样的话,当主线程结束时,比如因为按下了 KeyboardInterrupt,它就会自动退出。

设置为 daemon 后,当程序里只剩下 daemon 线程时,整个程序就会退出。

12

为了更详细地解释一下Aphex的回答,主线程几乎不可能捕捉到键盘中断信号,除非你的手指非常快。主线程几乎是立刻就退出了!你可以试试这个:

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秒后会自动退出——如果你愿意,可以把这个数字设得更大。你也可以使用忙循环来重复调用睡眠,但我觉得这样做没有必要。

撰写回答