我有以下捕获Ctrl+C的标准实现:
def signal_handler(signal, frame):
status = server.stop()
print("[{source}] Server Status: {status}".format(source=__name__.upper(),
status=status))
print("Exiting ...")
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
在server.start()
上,我正在启动CherryPy的线程实例。我创建了这个线程,认为可能因为CherryPy正在运行,所以主线程没有看到Ctrl+C这似乎没有任何影响,但发布了我现在拥有的代码:
__main__:
server.start()
服务器:
def start(self):
# self.engine references cherrypy.engine
self.__cherry_thread = threading.Thread(target=self.engine.start)
self.status['running'] = True
self.status['start_time'] = get_timestamp()
self.__cherry_thread.start()
def stop(self):
self.status['running'] = False
self.status['stop_time'] = get_timestamp()
self.engine.exit()
self.__thread_event.set()
return self.status
当我按下Ctrl+C时,应用程序不会停止。我在上面的signal_handler
中放置了一个断点,它永远不会被命中。
您最终想要实现什么还不太清楚,但看起来您错过了CherryPy设计的要点。
CherryPy状态和组件编排是围绕消息总线构建的。对于作为开发人员的您来说,这也是操作系统特定信号的抽象。所以,若你们想拥有一个线程,最好将其封装到CherryPy插件中,该插件将遵守服务器的状态。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
import logging
import cherrypy
from cherrypy.process.plugins import SimplePlugin
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
}
}
class ExamplePlugin(SimplePlugin):
_thread = None
_running = None
_sleep = None
def __init__(self, bus, sleep = 2):
SimplePlugin.__init__(self, bus)
self._sleep = sleep
def start(self):
'''Called when the engine starts'''
self.bus.log('Setting up example plugin')
# You can listen for a message published in request handler or
# elsewhere. Usually it's putting some into the queue and waiting
# for queue entry in the thread.
self.bus.subscribe('do-something', self._do)
self._running = True
if not self._thread:
self._thread = threading.Thread(target = self._target)
self._thread.start()
# Make sure plugin priority matches your design e.g. when starting a
# thread and using Daemonizer which forks and has priority of 65, you
# need to start after the fork as default priority is 50
# see https://groups.google.com/forum/#!topic/cherrypy-users/1fmDXaeCrsA
start.priority = 70
def stop(self):
'''Called when the engine stops'''
self.bus.log('Freeing up example plugin')
self.bus.unsubscribe('do-something', self._do)
self._running = False
if self._thread:
self._thread.join()
self._thread = None
def exit(self):
'''Called when the engine exits'''
self.unsubscribe()
def _target(self):
while self._running:
try:
self.bus.log('some periodic routine')
time.sleep(self._sleep)
except:
self.bus.log('Error in example plugin', level = logging.ERROR, traceback = True)
def _do(self, arg):
self.bus.log('handling the message: {0}'.format(arg))
class App:
@cherrypy.expose
def index(self):
cherrypy.engine.publish('do-something', 'foo')
return 'Look in the terminal or log'
if __name__ == '__main__':
ExamplePlugin(cherrypy.engine).subscribe()
cherrypy.quickstart(App(), '/', config)
更新
更明确的关于处理SIGINT信号。这是第一个链接的FSM图。
O
|
V
STOPPING --> STOPPED --> EXITING -> X
A A |
| ___ |
| |
| V V
STARTED <-- STARTING
您感兴趣的是STOPPING
或EXITING
,因为两者都与处理SIGINT有关。不同的是,STOPPING
可能会发生多次,例如,当服务器被守护时,SIGHUP会使其重新启动。所以你可以把你的终止程序放在ExamplePlugin.exit
中。