CherryPy干扰Windows上Twisted的关闭

7 投票
1 回答
2053 浏览
提问于 2025-04-15 12:38

我有一个应用程序,它通过在主线程中使用 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()

撰写回答