使用主线程中的KeyboardException中断Python中的线程

时间:2015-04-14 17:22:14

标签: python multithreading

我有一些看起来或多或少的类:

import threading
import time

class Foo():
    def __init__(self, interval, callbacks):
        self.thread = threading.Thread(target=self.loop)
        self.interval = interval
        self.thread_stop = threading.Event()
        self.callbacks = callbacks

    def loop():
        while not self.thread_stop.is_set():
            #do some stuff...
            for callback in self.callbacks():
                callback()
            time.sleep(self.interval)

    def start(self):
        self.thread.start()

    def kill(self):
        self.thread_stop.set()

我在主线程中使用的是这样的:

interval = someinterval
callbacks = [some callbacks]

f = Foo(interval, callbacks)

try:
    f.start()
except KeyboardInterrupt:
    f.kill()
    raise

我希望KeyboardInterrupt在所有回调完成后但在循环重复之前终止线程。目前它们被忽略了,我不得不求助于杀死程序运行的终端进程。

我看到了使用来自this post的threading.Event的想法,但看起来我做错了,并且正在使这个项目工作变得非常麻烦。

我不知道它是否相关,但回调我正在从Internet传递访问数据并大量使用重试装饰来处理不可靠的连接。


编辑

在大家的帮助下,现在循环现在看起来像Foo:

    def thread_loop(self):
        while not self.thread_stop.is_set():
            # do some stuff
            # call the callbacks
            self.thread_stop.wait(self.interval)

这是解决方案,虽然它并不理想。此代码在PythonAnywhere上运行,帐户的价格是CPU时间。我将不得不看到它在一天的过程中使用多少线程不断的醒来和睡眠,但它至少解决了主要问题

3 个答案:

答案 0 :(得分:3)

我认为您的问题是try-except - f.start()周围有一个阻止,但会立即返回,因此您不会在线程后抓住KeyboardInterrupt s开始了。

您可以尝试在程序底部添加while循环,如下所示:

f.start()
try:
    while True:
        time.sleep(0.1)
except KeyboardInterrupt:
    f.kill()
    raise

这不是最优雅的解决方案,但它应该有效。

答案 1 :(得分:1)

@ jazzpi的回答正确地解决了您在主线程中遇到的问题。

对于线索循环中的sleep,您只需拨打sleep即可将呼叫替换为self.thread_stop.wait(self.interval)

这样,一旦设置了停止事件,或者等待(即休眠)self.interval秒后,线程就会唤醒。 (Event docs

答案 2 :(得分:1)

感谢@ shx2和@jazzpi整理拼图的两个独立部分。

所以最终的代码是

import threading
import time

class Foo():
    def __init__(self, interval, callbacks):
        self.thread = threading.Thread(target=self.loop)
        self.interval = interval
        self.thread_stop = threading.Event()
        self.callbacks = callbacks

    def loop():
        while not self.thread_stop.is_set():
            #do some stuff...
            for callback in self.callbacks():
                callback()
            self.thread_stop.wait(self.interval)

    def start(self):
        self.thread.start()

    def kill(self):
        self.thread_stop.set()

然后在主

interval = someinterval
callbacks = [some, callbacks]
f = Foo(interval, callbacks)
f.start()
try:
    while True:
        time.sleep(0.1)
except KeyboardInterrupt:
    f.kill()
    raise