我正在从事一个编程项目——用 Python 编写一个基本的 P2P 文件共享应用程序。我使用两个线程:一个主线程用于调用 select 并等待来自套接字和 sys.stdin 列表的输入(以接收键入的命令),另一个辅助线程从队列中获取状态更新消息并打印它们。 (这是唯一打印任何内容的东西)
我还需要捕获标准 SIGINT 并处理它以优雅地退出。我有一个 quit 方法可以做到这一点;输入“quit”作为命令就可以了。因此,在主线程中,我尝试将此方法设置为 SIGINT 的处理程序。据我所知,该进程捕获信号并调用 quit 方法。辅助线程打印一条消息,确认其正在退出。但随后我从主线程收到以下错误消息:
Traceback (most recent call last):
File "peer.py", line 226, in <module>
main()
File "peer.py", line 223, in main
p.run()
File "peer.py", line 160, in run
readables, writables, exceptions = select(self.sockets, [], [])
select.error: (4, 'Interrupted system call')
之后程序仍然退出。如果没有信号处理程序,发送 SIGINT 会给出以下结果:
Traceback (most recent call last):
File "peer.py", line 225, in <module>
main()
File "peer.py", line 222, in main
p.run()
File "peer.py", line 159, in run
readables, writables, exceptions = select(self.sockets, [], [])
KeyboardInterrupt
无法终止程序;我必须停下来杀死它。这很令人困惑,因为 SIGINT 似乎仅在被我的自定义方法捕获时才会中断对 select 的调用。 (这只会在打印队列上放置一条消息并设置一个“完成”变量)有谁知道这是如何发生的?尝试同时使用信号处理程序和线程是一个坏主意吗?
I'm working on a programming project--writing a basic P2P filesharing application in Python. I'm using two threads: a main one to call select and wait for input from a list of sockets and sys.stdin (to receive typed commands) and a helper thread that takes status update messages off a queue and prints them. (It is the only thing that prints anything)
I'm also required to catch the standard SIGINT and handle it to exit gracefully. I have a quit method that does this; typing 'quit' as a command works just fine. So in the main thread I try setting this method as the handler for SIGINT. As far as I can tell, the process catches the signal and calls the quit method. The helper thread prints a message confirming that it is exiting. But then I get the following error message from the main thread:
Traceback (most recent call last):
File "peer.py", line 226, in <module>
main()
File "peer.py", line 223, in main
p.run()
File "peer.py", line 160, in run
readables, writables, exceptions = select(self.sockets, [], [])
select.error: (4, 'Interrupted system call')
After which the program does still exit. Whereas without the signal handler in place, sending a SIGINT gives me the following:
Traceback (most recent call last):
File "peer.py", line 225, in <module>
main()
File "peer.py", line 222, in main
p.run()
File "peer.py", line 159, in run
readables, writables, exceptions = select(self.sockets, [], [])
KeyboardInterrupt
Which fails to terminate the program; I have to stop and kill it. This is confusing because the SIGINT appears to interrupt the call to select only when it is caught by my custom method. (Which only puts a message on the print queue and sets a "done" variable) Does anyone know how this can be happening? Is it just a bad idea trying to use signal handlers and threads simultaneously?
发布评论
评论(2)
我不确定是否使用信号处理程序来捕获这种情况,但我在这里找到了在基于 *nix 的系统上处理这种情况的方法: http://code.activestate.com/recipes/496735-workaround-for-missed-sigint-in-multithreaded-prog/
简而言之(如果我理解正确的话):
在你之前启动任何新线程,分叉一个子进程(使用
os.fork
)来完成程序运行,并让父进程监视KeyboardInterrupt
。当父进程捕获键盘中断时,您可以使用 os.kill 终止子进程(现在可能已经启动了其他线程)。这将依次终止该子进程的所有线程。
I'm not sure about using signal handlers to catch this case, but I've found a recipe for handling this case on *nix based systems here: http://code.activestate.com/recipes/496735-workaround-for-missed-sigint-in-multithreaded-prog/
In a nutshell (If I undertand correctly):
Before you start any new threads, fork a child process (using
os.fork
) to finish the program run, and have the parent process watch for theKeyboardInterrupt
.When the parent catches the keyboard interrupt, you can kill the child process (which by now may have started other threads) using
os.kill
. This will, in turn, terminate any threads of that child process.是的,昨晚我停止工作后,我意识到我确实希望它中断。据推测,它是通过执行信号处理程序而被中断的。所以我只是捕获 select.error 并让它跳转到循环末尾,在那里它立即退出并继续执行清理代码。
Yes, last night after I stopped working on it I realized that I did want it to interrupt. It was being interrupted by executing the signal handler, presumably. So I just catch the select.error and have it jump to the end of the loop, where it immediately exits and moves on to the cleanup code.