如何在Python多线程程序中捕获SIGINT信号?

18

使用模块 threading 和类 Thread() 时,我无法捕获 SIGINT(在控制台中按下 Ctrl + C)。

为什么?我该怎么做?

简单的测试程序:

#!/usr/bin/env python

import threading

def test(suffix):
    while True:
        print "test", suffix

def main():
    for i in (1, 2, 3, 4, 5):
        threading.Thread(target=test, args=(i, )).start()

if __name__ == "__main__":
    main()

当我按下Ctrl + C时,没有任何反应。

2个回答

14

线程和信号不兼容。在Python中,这种情况比外部更为常见:信号只会发送到一个线程(即主线程);其他线程不会接收到该消息。你无法通过除主线程外的任何方式中断线程,它们超出了你的控制范围。

在这种情况下,唯一能做的就是在主线程和启动的任何线程之间引入一个通信渠道,使用queue模块。然后,你可以向线程发送一条消息,并在它看到该消息时终止它(或执行其他你想要的操作)。

或者,这通常是一个非常好的替代方案,你可以不使用线程。然而,使用什么来代替线程取决于你试图实现的目标。


3
主线程也没有收到 SIGINT 信号。否则我可以捕获它并调用 sys.exit(0) 来退出程序。 - Marko Kevac
似乎我错了。主线程确实收到了SIGINT信号。谢谢! - Marko Kevac
但是我不明白为什么 sys.exit(0) 在信号处理程序中无法工作。 - Marko Kevac
Python 正在等待您启动(使用 threading.Thread)的线程结束。您可以将这些线程设置为守护进程,以避免这种情况,但是这会导致错误提示声音,因为仍在运行的线程的 Python 环境被强制终止。您必须要求线程退出,或者不使用线程。 - Thomas Wouters

-1

基本上,您可以通过在工作期间读取队列来检查父进程是否发出了信号。如果父进程收到 SIGINT 信号,则通过队列(在这种情况下为任何内容)发出信号,子进程将完成其工作并退出...

def fun(arg1, thread_no, queue):
   while True:
    WpORK...
    if queue.empty() is False or errors == 0:
     print('thread ', thread_no, ' exiting...')
     with open('output_%i' % thread_no, 'w') as f:
      for line in lines: f.write(line)
     exit()

threads = []
for i, item in enumerate(items):
 threads.append( dict() )
 q = queue.Queue()
 threads[i]['queue'] = q
 threads[i]['thread'] = threading.Thread(target=fun, args=(arg1, i, q))
 threads[i]['thread'].start()
try:
 time.sleep(10000)
except:
 for thread in threads:
  thread['queue'].put('TERMINATING')

请考虑说明为什么这段代码能够正常运行。在StackOverflow上,通常不鼓励直接转载代码。 - rayryeng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接