守护进程中的Python多进程池

5 投票
1 回答
4921 浏览
提问于 2025-04-16 20:30

我在这里提了个问题,但得到的答案不够详细,没法解决我的问题(可能是因为我没有把问题描述得很清楚,所以我想改进一下):Python多进程守护进程中的僵尸进程

我想实现一个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

    ...

同样的,我没有重定向输入输出,这样我可以调试。这里的打印是为了检查工作池的位置。

还有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只是我随便尝试解决问题的一个方法,但并没有成功。起初我以为这是僵尸子进程的问题,因为我在while循环中使用了self.pool.close()self.pool.join(),同时又调用了os.kill(pid, SIGTERM)。这是在我决定通过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。此外,位置的变化似乎也影响了僵尸进程的数量。

目前,我还没有添加通过工作者运行任何东西的功能。我的问题似乎完全与如何正确设置工作池有关。如果有任何信息能帮助我解决这个问题,或者关于创建一个使用工作池来执行一系列命令的守护进程的建议,我将非常感激。因为我还没有做到这一点,所以我不知道接下来会遇到什么挑战。我在想我可能需要自己写一个工作池,但如果有什么好方法能让这个工作池正常工作,那就太好了。

1 个回答

1

解决办法是把 self.pool = Pool(process=4) 这行代码放在 daemonize 方法的最后一行。否则,这个池子可能会在某个地方“迷路”(可能是在 fork 的过程中)。这样的话,池子就可以在你想要让它后台运行的应用程序重写的 run 方法里面访问到。不过,在 stop 方法里是无法访问这个池子的,如果强行访问就会出现 NoneType 异常。我觉得还有更优雅的解决方案,但目前这个方法可以用,暂时就这样。如果我想让 stop 在池子还在运行的时候就失败,那我需要在 run 里添加一些额外的功能和某种形式的消息,但我现在不打算去处理这个问题。

撰写回答