python:杀死孩子或报告他们成功的简单方法?

Posted

技术标签:

【中文标题】python:杀死孩子或报告他们成功的简单方法?【英文标题】:python: simple approach to killing children or reporting their success? 【发布时间】:2011-03-24 21:02:42 【问题描述】:

我想要

    并行调用shell命令(例如下面的'sleep'), 报告他们的个人开始和完成情况并 能够使用“kill -9 parent_process_pid”杀死它们。

已经有很多关于这类事情的文章,但我觉得我还没有完全找到我正在寻找的优雅的 Pythonic 解决方案。对于完全不熟悉 python 的人,我还试图让内容相对可读(且简短)。

到目前为止,我的方法(参见下面的代码)是:

    将 subprocess.call(unix_command) 放入报告命令开始和完成的包装函数中。 使用 multiprocess.Process 调用包装函数。 跟踪适当的 pid,将它们全局存储,然后在 signal_handler 中终止它们。

我试图避免定期轮询进程的解决方案,但我不确定为什么。

有更好的方法吗?

import subprocess,multiprocessing,signal
import sys,os,time

def sigterm_handler(signal, frame):
        print 'You killed me!'
        for p in pids:
                os.kill(p,9)
        sys.exit(0)

def sigint_handler(signal, frame):
        print 'You pressed Ctrl+C!'
        sys.exit(0)

signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGTERM, sigterm_handler)

def f_wrapper(d):
        print str(d) + " start"
        p=subprocess.call(["sleep","100"])
        pids.append(p.pid)
        print str(d) + " done"

print "Starting to run things."

pids=[]

for i in range(5):
        p=multiprocessing.Process(target=f_wrapper,args=(i,))
        p.daemon=True
        p.start()

print "Got things running ..."

while pids:
        print "Still working ..."
        time.sleep(1)

【问题讨论】:

+1 以获得引人注目的标题。 我不知道 Python 可以杀死孩子...... @Zonda333:哦,当然。 from __future__ import murder. 【参考方案1】:

一旦subprocess.call返回,子进程就完成了——call的返回值就是子进程的returncode。因此,在列表pids 中累积这些返回码(顺便说一句,在附加它的多进程和“主”进程之间不同步)并将它们发送9 信号“好像”它们是进程 ID 而不是返回代码,肯定是错的。

另一个绝对错误的问题是规范:

可以用 'kill -9 杀死他们 parent_process_pid'。

因为-9 意味着父进程不可能拦截信号(这是明确指定-9目的)——我想-9 因此在这里是虚假的。

您应该使用threading 而不是multiprocessing(每个“保姆”线程或进程基本上什么都不做,只是等待它的子进程,那么为什么要在如此轻量级的任务上浪费进程呢?-);您还应该在主线程中调用suprocess.Process(以启动子进程并能够获取其.pid以放入列表中)并将生成的进程对象传递给等待它的保姆线程(和完成后报告并将其从列表中删除)。子进程 ID 列表应该由锁保护,因为主线程和几个保姆线程都可以访问它,并且集合可能比列表(更快的删除)更好,因为您不关心排序也不关心关于避免重复。

所以,大致(没有测试,所以可能存在错误;-)我会将您的代码更改为类似:

import subprocess, threading, signal
import sys, time

pobs = set()
pobslock = threading.Lock()
def numpobs():
    with pobslock:
        return len(pobs)

def sigterm_handler(signal, frame):
    print 'You killed me!'
    with pobslock:
        for p in pobs: p.kill()
    sys.exit(0)

def sigint_handler(signal, frame):
    print 'You pressed Ctrl+C!'
    sys.exit(0)

signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGTERM, sigterm_handler)

def f_wrapper(d, p):
    print d, 'start', p.pid
    rc = p.wait()
    with pobslock:
        pobs.remove(p)
    print d, 'done, rc =', rc

print "Starting to run things."

for i in range(5):
    p = subprocess.Popen(['sleep', '100'])
    with pobslock:
        pobs.add(p)
    t = threading.Thread(target=f_wrapper, args=(i, p))
    t.daemon=True
    t.start()

print "Got things running ..."

while numpobs():
    print "Still working ..."
    time.sleep(1)

【讨论】:

谢谢,这很有帮助。我会在清理完后回帖! @mathtick,不客气!我已经编辑了我的 A 以大致展示我如何看到代码结构最佳。 这 speculation.org/garrick/kill-9.html 对我来说是一个很好的教训。我想我是在第一次接触 linux 机器时养成了“-9”的习惯。 我在单独的答案中包含了上面代码的略微修改版本。 @Alex:为什么sigterm_handler 必须明确杀死所有守护线程,而sigint_handler 却没有?尽管有t.daemon=True,但似乎确实需要明确的p.kill。我也很困惑,即使 t.daemon=True 被注释掉,程序的行为(在 SIGTERM 或 SIGINT 之后)似乎也没有改变。 t.daemon=True的目的是什么?【参考方案2】:

这段代码(下面的代码)似乎对我有用,从“top”或命令行中的 ctrl-c 杀死。亚历克斯建议的唯一真正变化是将 subprocess.Process 替换为 subprocess.Popen 调用(我不认为 subprocess.Process 存在)。

这里的代码也可以通过某种方式锁定标准输出来改进,这样就不会在进程之间打印重叠。

import subprocess, threading, signal
import sys, time

pobs = set()                            # set to hold the active-process objects
pobslock = threading.Lock()     # a Lock object to make sure only one at a time can modify pobs

def numpobs():
        with pobslock:
                return len(pobs)

# signal handlers
def sigterm_handler(signal, frame):
        print 'You killed me! I will take care of the children.'
        with pobslock:
                for p in pobs: p.kill()
        sys.exit(0)

def sigint_handler(signal, frame):
        print 'You pressed Ctrl+C! The children will be dealt with automatically.'
        sys.exit(0)

signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGTERM, sigterm_handler)


# a function to watch processes
def p_watch(d, p):
        print d, 'start', p.pid
        rc = p.wait()
        with pobslock:
                pobs.remove(p)
        print d, 'done, rc =', rc


# the main code
print "Starting to run things ..."
for i in range(5):
        p = subprocess.Popen(['sleep', '4'])
        with pobslock:
                pobs.add(p)
        # create and start a "daemon" to watch and report the process p.
        t = threading.Thread(target=p_watch, args=(i, p))
        t.daemon=True
        t.start()

print "Got things running ..."
while numpobs():
        print "Still working ..."
        time.sleep(1)

【讨论】:

你是对的,没有 subprocess.Process -- 对不起,现在编辑我的 A 来修复它。

以上是关于python:杀死孩子或报告他们成功的简单方法?的主要内容,如果未能解决你的问题,请参考以下文章

06.极简主义——汇流(笔记)

关于极简主义融入到企业管理的思考

父母杀死孩子后,孩子的Linux /proc/PID dir仍然活着

QProcess 被杀死或成功完成

Jenkins 配置邮件发送测试报告

我正在尝试将我的多个数组方法调用简化为 if 语句或更简单的基于条件的语句