对一组Popen对象使用wait()

1 投票
3 回答
606 浏览
提问于 2025-04-18 08:19

我有一些 Popen 对象,每一个都代表我启动的一个长时间运行的命令。实际上,我并不指望这些命令会结束。如果其中任何一个命令结束了,我想等几秒钟再重新启动它。有没有什么好的、符合 Python 风格的方法来做到这一点呢?

比如说:

import random
from subprocess import Popen

procs = list()
for i in range(10):
    procs.append(Popen(["/bin/sleep", str(random.randrange(5,10))]))

一种简单的方法可能是:

for p in procs:
    p.wait()
    print "a process has exited"
    # restart code
print "all done!"

但这样做的话,我就无法及时知道哪个进程已经结束了。所以我可以尝试:

for p in procs:
    p.poll()
    if p.returncode is not None:
        print "a process has exited"
        procs.remove(p)
        # restart code
print "all done!"

不过,这样会形成一个紧密的循环,会占用 CPU 资源。我想我可以在循环里加一个 time.sleep(1),这样就不会一直忙着等待,但这样会失去一些精确度。

我觉得应该有一种更好的方法来等待一组进程 ID——我说得对吗?

3 个回答

0

Twisted进程API可以帮助我们高效地处理进程完成后的情况,以及其他很多条件。

1

如果你使用的是posix操作系统,你可以用os.wait来等待任何一个子进程。这个方法会返回一个进程ID,你可以把这个ID和你列表里的pid进行比较,这样就能找到已经结束的进程。

import random
from subprocess import Popen
import os

procs = {}
for i in range(10):
    proc = Popen(["/bin/sleep", str(random.randrange(5,10))])
    procs[proc.pid] = proc

while procs:
    pid, status = os.wait()
    proc = procs.pop(pid)
    print "process %d has exited" % proc.pid
    # restart code
print "all done!"
2
  1. “重启崩溃的服务器”这个任务非常常见,通常不需要用自定义代码来处理,除非有特别的理由。可以看看 upstartsystemdmonit 这些工具。

  2. multiprocessing.Pool 这个对象听起来不错——它会自动启动进程,甚至在需要的时候重新启动它们。不过,它的配置选项不太多。

这里有一个用老牌的 Popen 的解决方案:

import random, time
from subprocess import Popen


def work_diligently():
    cmd = ["/bin/sleep", str(random.randrange(2,4))]
    proc = Popen(cmd)
    print '\t{}\t{}'.format(proc.pid, cmd) # pylint: disable=E1101
    return proc


def spawn(num):
    return [ work_diligently() for _ in xrange(num) ]


NUM_PROCS = 3
procs = spawn(NUM_PROCS)
while True:
    print time.ctime(), 'scan'
    procs = [ 
        proc for proc in procs
        if proc.poll() is None
    ]
    num_exited = NUM_PROCS - len(procs)
    if num_exited:
        print 'Uhoh! Restarting {} procs'.format(num_exited)
        procs.extend( spawn(num_exited) )
    time.sleep(1)

输出:

    2340    ['/bin/sleep', '2']
    2341    ['/bin/sleep', '2']
    2342    ['/bin/sleep', '3']
Mon Jun  2 18:01:42 2014 scan
Mon Jun  2 18:01:43 2014 scan
Mon Jun  2 18:01:44 2014 scan
Uhoh! Restarting 2 procs
    2343    ['/bin/sleep', '3']
    2344    ['/bin/sleep', '2']
Mon Jun  2 18:01:45 2014 scan
Uhoh! Restarting 1 procs
    2345    ['/bin/sleep', '2']
Mon Jun  2 18:01:46 2014 scan
Uhoh! Restarting 1 procs
    2346    ['/bin/sleep', '2']
Mon Jun  2 18:01:47 2014 scan
Uhoh! Restarting 2 procs
    2347    ['/bin/sleep', '3']
    2349    ['/bin/sleep', '2']

撰写回答