从python守护进程启动线程的正确方法

时间:2014-06-26 06:58:20

标签: python multithreading daemon

我需要用web界面编写简单的守护进程。

我的想法是使用python-daemon package并在一个帖子中运行wsgiref.simple_server

守护程序可以正常使用以下代码:

import daemon
import logging
import time
import signal
import threading

logfilename = '/var/log/testdaemon.log'
logger = logging.getLogger("DaemonLog")
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
    '%(asctime)s:%(levelname)s:%(message)s',
    '%Y-%m-%d %H:%M:%S')
handler = logging.FileHandler(logfilename)
handler.setFormatter(formatter)
logger.addHandler(handler)

def initial_program_setup():
    logger.info('daemon started')

def do_main_program():
    while True:
        time.sleep(1)
        logger.info('another second passed')

def program_cleanup(signum, frame):
    logger.info('daemon stops')
    context.terminate(signum, frame)

def reload_program_config(signum, frame):
    logger.info('reloading config')

context = daemon.DaemonContext()

context.signal_map = {
    signal.SIGTERM: program_cleanup,
    signal.SIGHUP: 'terminate',
    signal.SIGUSR1: reload_program_config,
    }

context.files_preserve = [handler.stream]

initial_program_setup()

with context:
    do_main_program()

但是,如果我在initial_program_setup()开始这样的话题:

def web_gui():
    logger.info('weg gui started')

web = threading.Thread(target=web_gui)
web.setDaemon(True)

def initial_program_setup():
    logger.info('daemon started')
    web.start()

然后在线程完成后看起来像守护进程退出。添加类似

的内容
while True:
    time.sleep(1)

web_gui()(使线程永远运行,就像Web服务器一样)会使情况变得更糟:甚至行[{1}}也不会显示在日志中。

我的问题是:

  1. 为什么这不起作用?在守护进程中启动线程的正确方法是什么?
  2. 也许有更好的方法通过Web界面控制守护进程?有了这样的架构,我想我应该为每个界面页面开始新的线程,这很难扩展。
  3. 感谢。

2 个答案:

答案 0 :(得分:1)

这是守护程序库的限制(discussion thread starts here)。

TL; DR:你的选择是:

  • 不要使用守护进程,因为在这方面它是无法修复的。
  • 使用daemoncontext"在"中启动线程块。

长版:

当守护程序库切换到守护程序上下文时,它会执行双叉。这意味着它首先分叉然后杀死父进程。新的fork没有任何线程,因此退出父进程等同于杀死webgui线程。最终,此问题的任何解决方案都必须在新创建的子进程中启动任何永久线程。在守护程序上下文中执行此操作的缺点是您不再能够将潜在错误传播给用户。理想情况下,你是双叉,但不要退出父进程,然后设置你的守护进程,然后在进入主循环之前让父进程退出。使用守护程序库或任何实现PEP3143草稿的库无法实现这一点。

答案 1 :(得分:0)

为我工作。 你基本上不得不想象设置守护进程上下文就像一个额叶切除术:-)

以下是我认为很容易理解的内容 我的 main 仅解析参数,如果标记为在后台运行,则创建守护进程上下文。此后,它执行一个公共主循环函数_main_common(),这是完成所有操作的地方,包括正在创建的所有线程:

args = parse_arguments(args)
if args.foreground:
   # we run in the foreground, so just run the main loop
   _main_common()
else:
   # we run in the background, so now create the daemon context and run the main loop
   context = create_daemon_context(context, detach_process=True)
   with context:
      _main_common()