CherryPy干扰Windows上Twisted的关闭
我有一个应用程序,它通过在主线程中使用 reactor.run()
启动 Twisted,同时还启动了一些其他线程,包括 CherryPy 网络服务器。下面这个程序在 Linux 上按下 Ctrl+C 时能正常关闭,但在 Windows 上却不行:
from threading import Thread
from signal import signal, SIGINT
import cherrypy
from twisted.internet import reactor
from twisted.web.client import getPage
def stop(signum, frame):
cherrypy.engine.exit()
reactor.callFromThread(reactor.stop)
signal(SIGINT, stop)
class Root:
@cherrypy.expose
def index(self):
reactor.callFromThread(kickoff)
return "Hello World!"
cherrypy.server.socket_host = "0.0.0.0"
Thread(target=cherrypy.quickstart, args=[Root()]).start()
def print_page(html):
print(html)
def kickoff():
getPage("http://acpstats/account/login").addCallback(print_page)
reactor.run()
我认为问题出在 CherryPy 上,因为我写了一个没有 CherryPy 的不同程序,在 Linux 和 Windows 上按下 Ctrl+C 时都能正常关闭:
from time import sleep
from threading import Thread
from signal import signal, SIGINT
from twisted.internet import reactor
from twisted.web.client import getPage
keep_going = True
def stop(signum, frame):
global keep_going
keep_going = False
reactor.callFromThread(reactor.stop)
signal(SIGINT, stop)
def print_page(html):
print(html)
def kickoff():
getPage("http://acpstats/account/login").addCallback(print_page)
def periodic_downloader():
while keep_going:
reactor.callFromThread(kickoff)
sleep(5)
Thread(target=periodic_downloader).start()
reactor.run()
有没有人知道这是什么问题?这是我遇到的困惑:
- 在 Linux 上一切正常
- 在 Windows 上,当 CherryPy 不运行时,我可以通过
reactor.callFromThread
从信号处理器调用函数 - 但是当 CherryPy 正在运行时,从信号处理器使用
reactor.callFromThread
调用的任何函数都不会执行(我确认信号处理器本身确实被调用了)
我该怎么办呢?在运行 CherryPy 的情况下,如何从信号处理器关闭 Windows 上的 Twisted?这是个 bug,还是我只是错过了这两个项目中某些重要的文档部分?
1 个回答
14
CherryPy在你调用quickstart的时候,会自动处理一些信号。在你的情况下,可能只需要把quickstart的内容展开,实际上就几行代码,然后根据需要选择使用。下面是quickstart在主干代码中大致做了些什么:
if config:
cherrypy.config.update(config)
tree.mount(root, script_name, config)
if hasattr(engine, "signal_handler"):
engine.signal_handler.subscribe()
if hasattr(engine, "console_control_handler"):
engine.console_control_handler.subscribe()
engine.start()
engine.block()
在你的情况中,你不需要信号处理器,所以可以把它们省略掉。如果你不是从主线程启动CherryPy,也不需要调用engine.block。engine.block()的作用是让主线程不立即结束,而是等待进程结束(这样可以确保自动重载功能正常;有些平台在非主线程中调用execv会有问题)。
如果你去掉了block()的调用,甚至不需要把quickstart放在一个线程里。所以,把你的那行代码:
Thread(target=cherrypy.quickstart, args=[Root()]).start()
替换成:
cherrypy.tree.mount(Root())
cherrypy.engine.start()