Python多进程池在守护程序进程中的应用

4
我曾经提出了这个问题,但没有得到足够深入的解答以解决问题(最有可能是因为我没有严谨地解释我的问题,这也是我尝试纠正的)。链接如下:Zombie process in python multiprocessing daemon
我正在尝试实现一个使用工作池来执行命令的python守护进程,其中使用了Popen。我借鉴了http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/中的基本守护进程。
我只改变了initdaemonize(或同样的start)和stop方法。以下是对init方法所做的更改:
def __init__(self, pidfile):
#, stdin='/dev/null', stdout='STDOUT', stderr='STDOUT'):
    #self.stdin = stdin
    #self.stdout = stdout
    #self.stderr = stderr
    self.pidfile = pidfile
    self.pool = Pool(processes=4)

我没有设置标准输入、标准输出和标准错误输出,这样我就可以通过打印语句来调试代码。此外,我尝试将这个进程池移到几个位置,但只有这个位置不会产生异常。
以下是“daemonize”方法的更改:
def daemonize(self):
    ...

    # redirect standard file descriptors
    #sys.stdout.flush()
    #sys.stderr.flush()
    #si = open(self.stdin, 'r')
    #so = open(self.stdout, 'a+')
    #se = open(self.stderr, 'a+', 0)
    #os.dup2(si.fileno(), sys.stdin.fileno())
    #os.dup2(so.fileno(), sys.stdout.fileno())
    #os.dup2(se.fileno(), sys.stderr.fileno())

    print self.pool

    ...

同样的事情,我没有重定向io以便进行调试。这里使用print是为了检查池的位置。

stop方法也有所改变:

def stop(self):
    ...

    # Try killing the daemon process
    try:
        print self.pool
        print "closing pool"
        self.pool.close()
        print "joining pool"
        self.pool.join()
        print "set pool to None"
        self.pool = None
        while 1:
            print "kill process"
            os.kill(pid, SIGTERM)

    ...

这里的想法是我不仅需要杀死进程,还需要清理池。 self.pool = None只是一次随机尝试解决问题,但没有起作用。起初我认为这是僵尸子进程的问题,当我将self.pool.close()self.pool.join()放在带有os.kill(pid, SIGTERM)的while循环内时出现了这个问题。在开始查看通过print self.pool查看池位置之前,就已经发生了这种情况。在这之后,我相信当守护程序启动和停止时,池不是同一个。下面是一些输出:

me@pc:~/pyCode/jobQueue$ sudo ./jobQueue.py start
<multiprocessing.pool.Pool object at 0x1c543d0>
me@pc:~/pyCode/jobQueue$ sudo ./jobQueue.py stop
<multiprocessing.pool.Pool object at 0x1fb7450>
closing pool
joining pool
set pool to None
kill process
kill process
... [ stuck in infinite loop]

不同的物体位置表明它们不是同一个池,其中一个可能是僵尸?
在使用“CTRL+C”后,以下是我从“ps aux|grep jobQueue”获得的内容:
root     21161  0.0  0.0  50384  5220 ?        Ss   22:59   0:00 /usr/bin/python ./jobQueue.py start
root     21162  0.0  0.0      0     0 ?        Z    22:59   0:00 [jobQueue.py] <defunct>
me       21320  0.0  0.0   7624   940 pts/0    S+   23:00   0:00 grep --color=auto jobQueue

我尝试将self.pool = Pool(processes=4)移动到不同的位置。如果将其移动到start()daemonize()方法中,print self.pool会抛出一个异常,说它是NoneType。此外,位置似乎会改变出现的僵尸进程数。
目前,我还没有添加通过工作进程运行任何内容的功能。我的问题似乎完全与正确设置工作进程池有关。我会感激任何能帮助解决这个问题或关于创建使用Popen执行一系列命令的守护程序服务并使用工作进程池的建议。因为我还没有做到那一步,所以我不知道接下来会面临什么挑战。我想我可能需要编写自己的进程池,但如果有一个好的技巧可以使进程池在这里工作,那就太棒了。

我想我已经知道我需要做什么,但我不知道如何做。在守护进程中,它会写入一个pid文件。每当调用启动或停止时,它会从文件中获取守护进程的PID。我需要用池进程的PID做同样的事情,但是怎么做呢? - Aaron Robinson
1个回答

1
解决方案是将self.pool = Pool(process=4)放在daemonize方法的最后一行。否则,池会在某个地方丢失(可能在fork中)。然后,池可以在由您希望使其成为守护进程的应用程序重载的run方法中访问。但是,在停止方法中无法访问池,这样做会导致NoneType异常。我相信有更优雅的解决方案,但这个解决方案有效,并且现在这就是我所拥有的。如果我希望在池仍在运行时使stop失败,我将不得不向run添加其他功能和某种形式的消息,但我目前并不关心这个问题。

唉,其实很简单,我只需要将 self.pool = Pool(process=4) 放在守护进程的 run 方法中即可,但这仍然无法让我使用 closejoin 方法来清理池。如果有其他解决方案,我会非常感激。 - Aaron Robinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接