为什么没有捕获CTRL-C并调用signal_handler



我有以下捕获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

您感兴趣的是STOPPINGEXITING,因为两者都与处理SIGINT有关。不同的是,STOPPING可能会发生多次,例如,当服务器被守护时,SIGHUP会使其重新启动。所以你可以把你的终止程序放在ExamplePlugin.exit中。

最新更新