如何通过subprocess停止单个脚本生成的所有子进程
我需要对我的包中的一部分运行一个 unittest
,最好的方法是控制它的启动,然后结束所有由 multiprocessing
模块产生的进程。
下面是我想说的内容:
test.py
import logging
import multiprocessing
import time
import random
log = logging.getLogger(__name__)
def start_consumers(conf, worker_count=5):
manager = WorkerManager(conf, worker_count)
manager.start()
class WorkerManager():
def __init__(self, conf, worker_count=5):
self.workers = []
for num in range(worker_count):
self.workers.append(WorkHandler(conf))
def start(self):
for worker in self.workers:
worker.daemon = True
worker.start()
print 'started'
for worker in self.workers:
worker.join()
class WorkHandler(multiprocessing.Process):
def __init__(self, conf, *args, **kwargs):
super(WorkHandler, self).__init__(*args, **kwargs)
self.conf = conf
self.name = str(random.randint(0,100))
def run(self):
while True:
print self.conf['foo'], self.name
time.sleep(3)
if __name__ == "__main__":
conf = {'foo': 'bar'}
start_consumers(conf)
现在如果我从Linux终端运行这个测试,我可以看到打印的语句。如果我执行:ps aux | grep python
,我可以看到所有生成的子进程:
sergey 4081 0.0 0.1 71684 13164 pts/3 S+ 08:58 0:00 python
sergey 4108 0.3 0.0 39092 6472 pts/3 S+ 08:59 0:00 python test.py
sergey 4109 0.0 0.0 39092 4576 pts/3 S+ 08:59 0:00 python test.py
sergey 4110 0.0 0.0 39092 4568 pts/3 S+ 08:59 0:00 python test.py
sergey 4111 0.0 0.0 39092 4576 pts/3 S+ 08:59 0:00 python test.py
sergey 4112 0.0 0.0 39092 4584 pts/3 S+ 08:59 0:00 python test.py
sergey 4113 0.0 0.0 39092 4580 pts/3 S+ 08:59 0:00 python test.py
sergey 4115 0.0 0.0 13588 944 pts/7 S+ 08:59 0:00 grep --color=auto python
现在如果我尝试用 subprocess
来运行同样的 test.py
,一切都正常,直到我需要结束它们为止:
>>> import subprocess as s
>>> p = s.Popen(['python', 'test.py'], stdout=s.PIPE)
问题:
有什么优雅的方法可以处理这个变量 p
,以便在我按下终端中的 Ctrl+C
时实现类似的行为?
换句话说,我想要的结果类似于 pkill -f "test.py"
。
更新:
p.kill()
或 p.terminate()
并不能满足我的需求:
这是我执行其中任何一个后的结果:
sergey 4438 0.0 0.0 0 0 pts/3 Z+ 09:16 0:00 [python] <defunct>
sergey 4439 0.0 0.0 39092 4572 pts/3 S+ 09:16 0:00 python test.py
sergey 4440 0.0 0.0 39092 4568 pts/3 S+ 09:16 0:00 python test.py
sergey 4441 0.0 0.0 39092 4576 pts/3 S+ 09:16 0:00 python test.py
sergey 4442 0.0 0.0 39092 4580 pts/3 S+ 09:16 0:00 python test.py
sergey 4443 0.0 0.0 39092 4580 pts/3 S+ 09:16 0:00 python test.py
2 个回答
1
你可以尝试创建一个新的会话,并使用 os.killpg()
来结束所有子进程:
import os
import signal
from subprocess import Popen
p = Popen('python test.py', shell=True, preexec_fn=os.setsid)
# ... later
os.killpg(p.pid, signal.SIGTERM) # send signal to the process group
查看这个链接了解更多信息:如何终止一个使用 shell=True 启动的 Python 子进程。
2
我觉得你想要的功能是 subprocess.terminate()
和 subprocess.kill()
这两个方法,它们属于 子进程对象。
你可能还需要结合一个在 atexit 模块中注册的方法,这个方法会遍历你所有的子进程并将它们终止。比如:
def terminate_children(children):
for process in children:
process.terminate()
...
# Somewhere else in your code
children = [s.Popen(['python', 'test.py'], stdout=s.PIPE) for i in range(number_processes)] # Spools up number_processes child processes
atexit.register(terminate_children, children) # where children is a list of subprocesses
这样做的话,当你优雅地终止父进程时,所有的子进程也会被优雅地终止。
如果你想从一个完全不同的脚本中杀掉这些进程,而没有直接引用子进程的代码,可以看看 这里。你基本上需要查看 Python 的 os
进程管理方法。