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

发布于 2024-09-12 12:04:14 字数 1301 浏览 11 评论 0原文

我想

  1. 并行调用 shell 命令(例如下面的“sleep”),
  2. 报告它们各自的启动和完成情况,并
  3. 能够使用“kill -9parent_process_pid”杀死它们。

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

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

  1. 将 subprocess.call(unix_command) 放入报告命令的开始和完成的包装函数中。
  2. 使用 multiprocess.Process 调用包装函数。
  3. 跟踪适当的 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)

I want to

  1. call shell commands (for example 'sleep' below) in parallel,
  2. report on their individual starts and completions and
  3. be able to kill them with 'kill -9 parent_process_pid'.

There is already a lot written on these kinds of things already but I feel like I haven't quite found the elegant pythonic solution I'm looking for. I'm also trying to keep things relatively readable (and short) for someone completely unfamiliar with python.

My approach so far (see code below) has been:

  1. put subprocess.call(unix_command) in a wrapper function that reports the start and completion of the command.
  2. call the wrapper function with multiprocess.Process.
  3. track the appropriate pids, store them globally, and kill them in the signal_handler.

I was trying to avoid a solution that periodically polled the processes but I'm not sure why.

Is there a better approach?

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

独木成林 2024-09-19 12:04:14

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

这个问题的另一件事肯定是错误的,那就是规范:

能够用“kill -9”杀死他们
Parent_process_pid'。

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

您应该使用线程而不是多处理(每个“保姆”线程或进程基本上除了等待其子进程之外什么都不做,那么为什么要在这样的进程上浪费进程呢?轻量级任务?-);您还应该在主线程中调用 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)

Once subprocess.call returns, the sub-process is done -- and call's return value is the sub-process's returncode. So, accumulating those return codes in list pids (which btw is not synced between the multi-process appending it, and the "main" process) and sending them 9 signals "as if" they were process ids instead of return codes, is definitely wrong.

Another thing with the question that's definitely wrong is the spec:

be able to kill them with 'kill -9
parent_process_pid'.

since the -9 means the parent process can't possibly intercept the signal (that's the purpose of explicitly specifying -9) -- I imagine the -9 is therefore spurious here.

You should be using threading instead of multiprocessing (each "babysitter" thread, or process, does essentially nothing but wait for its sub-process, so why waste processes on such a lightweight task?-); you should also call suprocess.Process in the main thread (to get the sub-process started and be able to obtain its .pid to put in the list) and pass the resulting process object to the babysitter thread which waits for it (and when it's done reports and removes it from the list). The list of subprocess ids should be guarded by a lock, since the main thread and several babysitter threads can all access it, and a set would probably be a better choice than a list (faster removals) since you don't care about ordering nor about avoiding duplicates.

So, roughly (no testing, so there might be bugs;-) I'd change your code to s/thing like:

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)
昵称有卵用 2024-09-19 12:04:14

这段代码(下面的代码)似乎对我有用,可以从命令行中通过“top”或 ctrl-c 进行终止。 Alex 建议中唯一真正的改变是用 subprocess.Popen 调用替换 subprocess.Process (我不认为 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)

This code (code below) seems to work for me, killing from "top" or ctrl-c from the command line. The only real change from Alex's suggestions was to replace subprocess.Process with a subprocess.Popen call (I don't think subprocess.Process exists).

The code here could also be improved by somehow locking stdout so that there is no chance of printing overlap between processes.

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文