【问题标题】:Error while using multiprocessing module in a python daemon在 python 守护程序中使用多处理模块时出错
【发布时间】:2010-11-24 11:49:22
【问题描述】:

在 python 守护进程中使用 multiprocessing 模块时出现以下错误(使用 python-daemon):

回溯(最近一次通话最后): _run_exitfuncs 中的文件“/usr/local/lib/python2.6/atexit.py”,第 24 行 函数(*目标,**卡格斯) _exit_function 中的文件“/usr/local/lib/python2.6/multiprocessing/util.py”,第 262 行 对于 active_children() 中的 p: 文件“/usr/local/lib/python2.6/multiprocessing/process.py”,第 43 行,在 active_children _清理() _cleanup 中的文件“/usr/local/lib/python2.6/multiprocessing/process.py”,第 53 行 如果 p._popen.poll() 不是无: 轮询中的文件“/usr/local/lib/python2.6/multiprocessing/forking.py”,第 106 行 pid, sts = os.waitpid(self.pid, flag) OSError: [Errno 10] 没有子进程

守护进程(父进程)生成多个进程(子进程),然后定期轮询这些进程以查看它们是否已完成。如果父进程检测到其中一个进程已完成,则它会尝试重新启动该进程。正是在这一点上引发了上述异常。似乎一旦其中一个进程完成,任何涉及多处理模块的操作都会生成此异常。如果我在非守护进程 python 脚本中运行相同的代码,它执行时不会出现任何错误。

编辑:

示例脚本

from daemon import runner

class DaemonApp(object):
    def __init__(self, pidfile_path, run):
        self.pidfile_path = pidfile_path
        self.run = run

        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'

def run():
    import multiprocessing as processing
    import time
    import os
    import sys
    import signal

    def func():
        print 'pid: ', os.getpid()
        for i in range(5):
            print i
            time.sleep(1)

    process = processing.Process(target=func)
    process.start()

    while True:
        print 'checking process'
        if not process.is_alive():
            print 'process dead'
            process = processing.Process(target=func)
            process.start()
        time.sleep(1)

# uncomment to run as daemon
app = DaemonApp('/root/bugtest.pid', run)
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

#uncomment to run as regular script
#run()

【问题讨论】:

    标签: python daemon multiprocessing


    【解决方案1】:

    我认为不久前在trunk和2.6 maint中进行了修复,这应该有助于解决这个问题。您可以尝试在python-trunk或最新的2.6-maint svn中运行您的脚本吗?我无法提取错误信息

    【讨论】:

    • 使用 python 2.7 主干运行脚本会产生相同的结果。我已将我正在使用的测试脚本添加到原始帖子中。我是不是做错了什么?
    • 我不确定,我必须加载一个使用 python-daemon 的测试来检查它。此刻我什么都没有。
    【解决方案2】:

    看起来你的错误出现在你的过程的最后——你的线索在你追溯的最开始,我引用...:

    File "/usr/local/lib/python2.6/atexit.py", line 24, in _run_exitfuncs
        func(*targs, **kargs)
    

    如果atexit._run_exitfuncs 正在运行,这清楚地表明您自己的进程正在终止。因此,从某种意义上说,错误本身只是一个小问题——只是来自multiprocessing 模块注册以从您的进程“退出”运行的某些功能。真正有趣的问题是,为什么你的主进程退出了?我认为这可能是由于一些未捕获的异常:尝试设置异常挂钩并显示丰富的诊断信息,然后再因多处理为退出运行而注册的其他异常而丢失...

    【讨论】:

    • 我将有问题的语句包装在 try..except 块中,我得到以下回溯: Traceback(最近一次调用最后一次):文件“bugtest.py”,第 32 行,如果未处理,则运行中.is_alive():文件“/usr/local/lib/python2.6/multiprocessing/process.py”,第 132 行,在 is_alive self._popen.poll() 文件“/usr/local/lib/python2.6/ multiprocessing/forking.py",第 106 行,在 poll pid 中,sts = os.waitpid(self.pid, flag) OSError: [Errno 10] No child processes
    • 我的猜测是多处理模块在某种程度上不同意守护进程双叉。不幸的是,我对这个材料的理解不够好,无法调试它。
    【解决方案3】:

    我在使用 RHEL 5.3 和 Python 2.6 下的 celery 分布式任务管理器时也遇到了这个问题。我的回溯看起来有点不同,但错误相同:

          File "/usr/local/lib/python2.6/multiprocessing/pool.py", line 334, in terminate
        self._terminate()
      File "/usr/local/lib/python2.6/multiprocessing/util.py", line 174, in __call__
        res = self._callback(*self._args, **self._kwargs)
      File "/usr/local/lib/python2.6/multiprocessing/pool.py", line 373, in _terminate_pool
        p.terminate()
      File "/usr/local/lib/python2.6/multiprocessing/process.py", line 111, in terminate
        self._popen.terminate()
      File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 136, in terminate
        if self.wait(timeout=0.1) is None:
      File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 121, in wait
        res = self.poll()
      File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 106, in poll
        pid, sts = os.waitpid(self.pid, flag)
    OSError: [Errno 10] No child processes
    

    非常令人沮丧..我现在正在通过 pdb 运行代码,但还没有发现任何东西。

    【讨论】:

      【解决方案4】:

      原始示例脚本具有“导入信号”但没有使用信号。但是,我有一个导致此错误消息的脚本,这是由于我的信号处理造成的,因此我将在此处进行解释,以防其他人发生这种情况。在信号处理程序中,我正在处理进程(例如创建一个新进程)。显然这不起作用,所以我停止在处理程序中这样做并修复了错误。 (注意:sleep() 函数在信号处理后唤醒,因此如果您需要对进程执行操作,这可以作为对信号进行操作的另一种方法)

      【讨论】:

        【解决方案5】:

        您的问题是守护程序和多处理模块之间的冲突,特别是在处理 SIGCLD(子进程终止)信号时。守护进程在启动时将 SIGCLD 设置为 SIG_IGN,这至少在 Linux 上会导致终止的子进程立即被收割(而不是在父进程调用 wait() 之前成为僵尸)。但是 multiprocessing 的 is_alive 测试调用 wait() 来查看进程是否处于活动状态,如果进程已经被回收,则失败。

        最简单的解决方案就是将 SIGCLD 设置回 SIG_DFL(默认行为——忽略信号并让父进程 wait() 用于终止的子进程):

        def run():
            # ...
        
            signal.signal(signal.SIGCLD, signal.SIG_DFL)
        
            process = processing.Process(target=func)
            process.start()
        
            while True:
                # ...
        

        【讨论】:

        • 再次使用“终止”、“收割”、“僵尸”和“收割”等词的奖励积分。
        【解决方案6】:

        忽略SIGCLD 也会导致subprocess 模块出现问题,因为该模块中存在错误(issue 1731717,截至 2011 年 9 月 21 日仍然打开)。

        此行为在python-daemon 库的version 1.4.8 中得到解决;它现在省略了对SIGCLD 的默认摆弄,因此不再与其他标准库模块发生这种不愉快的交互。

        【讨论】:

          猜你喜欢
          • 2023-03-10
          • 1970-01-01
          • 2014-11-25
          • 2017-04-03
          • 1970-01-01
          • 2015-03-27
          • 2011-09-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多