在 Python SocketServer 的异步(线程)示例中:http://docs.python.org/2/library/socketserver.html 启动了一个服务器线程(称为 server_thread),以便为每个请求启动新的线程。由于捕获 KeyboardInterrupts 出现了一些问题,我开始寻找类似的代码,发现如果不使用 server_thread,实际上 Ctrl-C 会起作用,没有明显区别。尽管我的代码可以正常运行,但我还是想了解:
- 为什么在使用 server_thread 时,一个简单的
try
无法捕获 KeyboardInterrupt? - 示例中的 server_thread 有什么用,与我相对简单的示例相比?
以下是来自 Python SocketServer 示例的代码,其中的 try
无法捕获 KeyboardInterrupt:
if __name__ == "__main__":
server = ThreadedTCPServer(serverAddr, SomeCode)
# ...
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
server_thread.start()
以下是我的简单示例,其中 Ctrl-C 可以正常工作:
if __name__ == "__main__":
server = ThreadedTCPServer(serverAddr, SomeCode)
try:
server.serve_forever()
print("Ctrl-C to exit")
except KeyboardInterrupt:
print("Interrupt received, exiting")
server.shutdown()
解决方案
答案 1:
- 这是一个普遍的问题。当你按
Ctrl-C
时,一个信号会被发送到进程中。在这个进程中,主线程会捕获信号(如果没有正确处理),主线程就会被打断。但是这个信号并不会杀死其他线程。并且,只要有非守护线程在运行,Python 就会一直运行(因为这并不安全)。如果你知道自己在做什么,可以添加以下代码:
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
现在应该可以正常工作了(假设你在 server_thread.start()
之后做了一些事情,比如等待,否则 Python 就会退出,不会等待守护线程)。但是请记住,你可能会在某些重要的操作期间杀死你的服务器。为了避免这种情况,你应该实现某种形式的优雅终止:
import signal
if __name__ == "__main__":
server = ThreadedTCPServer(serverAddr, SomeCode)
# 一些代码
server_thread = threading.Thread(target=server.serve_forever)
server_thread.start()
# 一些代码
try:
signal.pause() # 等待信号,可以一个循环?
except:
server.shutdown() # 优雅退出
- 它只是在单独的线程中启动了一个服务器。也许这个想法是你可以在此期间执行其他操作?如果你只想运行服务器,就没有必要这样做。
另外,原因可能是上面提到的那个:优雅退出。如果你只是中断服务器,它就会停止,也许是在某些重要的操作期间。
答案 2:
在 server-thread 示例中,你启动了一个提供内容的新线程。
此线程不是作为守护进程启动的(deamon = False)。这意味着程序在 server_forever()
完成之前不会退出。
因此,主线程什么也不做,程序等待非守护线程关闭。
我唯一看到区别是,这个新线程不是主线程,因此不会处理主线程处理的 KeyboardInterrupt 或其他事情。
如果你想将 GUI 与你的服务器集成,这可能很有用。GUI 可以并行运行。