Python:简单方法终止子进程或报告其成功?
我想要做的事情是:
- 同时运行一些命令(比如下面的'sleep'命令),
- 报告每个命令的开始和结束情况,
- 能够通过'kill -9 parent_process_pid'来终止它们。
关于这些事情已经有很多资料了,但我觉得我还没找到我想要的优雅的Python解决方案。我也希望能让这些内容对完全不懂Python的人来说相对容易理解(而且简短)。
到目前为止,我的做法是(见下面的代码):
- 把subprocess.call(unix_command)放在一个包装函数里,这个函数会报告命令的开始和结束。
- 用multiprocess.Process来调用这个包装函数。
- 跟踪合适的进程ID,把它们存储在全局变量里,并在信号处理器中终止它们。
我试图避免那种定期检查进程状态的解决方案,但我也不太确定为什么。
有没有更好的方法呢?
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)
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.call
执行完毕,子进程就结束了,而 call
的返回值就是子进程的 returncode
。所以,把这些返回码放到列表 pids
中(顺便说一下,这个列表在多进程中是不同步的,也就是说,添加它的多个进程和“主”进程之间的数据是不一致的),然后把它们当作进程ID发送 9
信号,这样做肯定是错的。
还有一个问题是,问题的描述也有问题:
应该能够用 'kill -9 parent_process_pid' 来终止它们。
因为 -9
的意思是父进程根本无法拦截这个信号(这就是明确指定 -9
的目的)——我想这里的 -9
是多余的。
你应该使用 threading
而不是 multiprocessing
(每个“看护者”线程或进程基本上只是等待它的子进程,所以为什么要在这么轻量的任务上浪费进程呢?);你还应该在主线程中调用 subprocess.Process
(这样可以启动子进程,并获得它的 .pid
,然后放入列表中),并把得到的进程对象传给看护者线程,让它等待这个进程(当进程结束时,它会报告并从列表中移除)。子进程ID的列表应该用锁来保护,因为主线程和多个看护者线程都可以访问它,而且使用集合(set)可能比列表(list)更好(因为删除速度更快),因为你不关心顺序,也不需要避免重复。
所以,大致上(没有测试,可能会有bug;-) 我会把你的代码改成类似这样的:
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)