Python 之 捕获 ctrl+c 结束多线程程序



最近工作中有个需求, 写一个 Python 脚本, 运行后需要响应 ctrl+c 的组合键来结束进程。


由于我的程序中创建了别的程序中的实例, 并给了回调函数, 导致它变成了一个多线程的程序。 当想要使用 ctrl+c 结束该程序的时候, 它根本不响应这组合键。

如果使用 ctrl+z 它倒是可以响应, 看着也像是结束了这个程序的运行, 但是使用 ps 一查, 该进程并没有结束, 而是被切换到了后台运行, 使用 ps -T 可以看到它有多个线程, 使用 fg 还可以将其从后台运行给切回来。

在网上找了很多资料和博客, 绝大多数的说法都是给一个全局 Boolean 类型变量, 然后将多线程设置为守护模式, 还需要使用 single 发送信号去修改这个全局变量, 其他的线程响应这个全局变量, 最后完成结束这个程序的目标。

但是我的程序的问题在于, 由于其他的线程不是我主动创建的, 无法在其运行前设置其为守护模式, 所以这种方式的基本条件对于我的程序并不成立。

最终我终于找到了一个 博客, 可以彻底的结束多线程的 Python 进程, 记录一下。

在代码中新增一个类:

class Watcher():
    # 解决使用 ctrl+c 不能杀死程序的问题

    def __init__(self):
        self.child = os.fork()
        if self.child == 0:
            return
        else:
            self.watch()

    def watch(self):
        try:
            os.wait()
        except KeyboardInterrupt:
            self.kill()
        os._exit(0)

    def kill(self):
        try:
            os.kill(self.child, signal.SIGKILL)
        except OSError:
            pass

然后, 在程序运行伊始便创建一个 Watcher 的实例:

if __name__ == "__main__":
	Watcher()
	do_anything_you_want()

这样, 在程序的运行过程中, 可以随时响应 ctrl+c 的键盘输入, 然后彻底的结束该程序的运行。 它的原理是:用另外一个进程来接受信号后杀掉执行任务进程。

原博客中, watch() 方法中的退出使用的是 sys.exit(), 我试过还是退不出去, 就换成了 os._exit(0) 的方式进行退出。