【问题标题】:Proper way to start thread from python daemon从 python 守护进程启动线程的正确方法
【发布时间】:2014-06-26 06:58:20
【问题描述】:

我需要编写简单的带有 Web 界面的守护进程。

这个想法是使用python-daemon package 并在一个线程中运行wsgiref.simple_server

Daemon 使用以下代码可以正常工作:

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 gui started 行也不会出现在日志中。

我的问题是:

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

谢谢。

【问题讨论】:

    标签: python multithreading daemon


    【解决方案1】:

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

    TL;DR:您的选择是:

    • 不要使用守护进程,因为它在这方面已经无法修复。
    • 在“with daemoncontext”块中启动线程。

    长版:

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

    【讨论】:

    【解决方案2】:

    为我工作。 您基本上必须想象设置守护进程上下文就像是额叶切除术 :-)

    这是我认为很容易理解的内容 我的 ma​​in 只解析参数,如果标记为在后台运行,则创建守护进程上下文。此后,它执行一个通用的主循环函数 _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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-13
      相关资源
      最近更新 更多