[英]running several system commands in parallel in Python
我编写了一个简单的脚本,对一系列文件执行系统命令。 为了加快速度,我想并行运行它们,但不是一次全部运行 - 我需要控制同时运行的命令的最大数量。 什么是最简单的方法来解决这个问题?
如果你正在调用子进程,我认为不需要使用线程池。 使用subprocess
模块的基本实现是
import subprocess
import os
import time
files = <list of file names>
command = "/bin/touch"
processes = set()
max_processes = 5
for name in files:
processes.add(subprocess.Popen([command, name]))
if len(processes) >= max_processes:
os.wait()
processes.difference_update([
p for p in processes if p.poll() is not None])
在Windows上, os.wait()
不可用(也没有任何其他等待任何子进程终止的方法)。 您可以通过在特定时间间隔内轮询来解决此问题:
for name in files:
processes.add(subprocess.Popen([command, name]))
while len(processes) >= max_processes:
time.sleep(.1)
processes.difference_update([
p for p in processes if p.poll() is not None])
睡眠时间取决于子过程的预期执行时间。
Sven Marnach的回答几乎是对的,但是有一个问题。 如果最后一个max_processes进程结束,主程序将尝试启动另一个进程,for循环将结束。 这将关闭主进程,进而可以关闭子进程。 对我来说,这种行为发生在screen命令中。
Linux中的代码将是这样的(并且只适用于python2.7):
import subprocess
import os
import time
files = <list of file names>
command = "/bin/touch"
processes = set()
max_processes = 5
for name in files:
processes.add(subprocess.Popen([command, name]))
if len(processes) >= max_processes:
os.wait()
processes.difference_update(
[p for p in processes if p.poll() is not None])
#Check if all the child processes were closed
for p in processes:
if p.poll() is None:
p.wait()
您需要将Semaphore对象与线程组合在一起。 信号量是一个对象,它允许您限制在给定代码段中运行的线程数。 在这种情况下,我们将使用信号量来限制可以运行os.system调用的线程数。
首先,我们导入我们需要的模块:
#!/usr/bin/python
import threading
import os
接下来,我们创建一个Semaphore对象。 这里的第四个是可以一次获取信号量的线程数。 这限制了可以一次运行的子进程数。
semaphore = threading.Semaphore(4)
此函数只是在调用Semaphore时将调用包装到子进程中。
def run_command(cmd):
semaphore.acquire()
try:
os.system(cmd)
finally:
semaphore.release()
如果您使用的是Python 2.6+,则可以变得更加简单,因为您可以使用'with'语句来执行获取和释放调用。
def run_command(cmd):
with semaphore:
os.system(cmd)
最后,为了表明这是按预期工作的,我们将八次调用“sleep 10”命令。
for i in range(8):
threading.Thread(target=run_command, args=("sleep 10", )).start()
使用'time'程序运行脚本表明,只需要20秒就可以并行运行两个四个睡眠。
aw@aw-laptop:~/personal/stackoverflow$ time python 4992400.py
real 0m20.032s
user 0m0.020s
sys 0m0.008s
我将Sven和Thuener的解决方案合并到一个等待尾随进程的解决方案中,如果其中一个进程崩溃,它也会停止:
def removeFinishedProcesses(processes):
""" given a list of (commandString, process),
remove those that have completed and return the result
"""
newProcs = []
for pollCmd, pollProc in processes:
retCode = pollProc.poll()
if retCode==None:
# still running
newProcs.append((pollCmd, pollProc))
elif retCode!=0:
# failed
raise Exception("Command %s failed" % pollCmd)
else:
logging.info("Command %s completed successfully" % pollCmd)
return newProcs
def runCommands(commands, maxCpu):
processes = []
for command in commands:
logging.info("Starting process %s" % command)
proc = subprocess.Popen(shlex.split(command))
procTuple = (command, proc)
processes.append(procTuple)
while len(processes) >= maxCpu:
time.sleep(.2)
processes = removeFinishedProcesses(processes)
# wait for all processes
while len(processes)>0:
time.sleep(0.5)
processes = removeFinishedProcesses(processes)
logging.info("All processes completed")
这个答案与此处提供的其他答案非常相似,但它使用的是列表而不是集合。 由于某些原因,当使用这些答案时,我收到有关集合更改大小的运行时错误。
from subprocess import PIPE
import subprocess
import time
def submit_job_max_len(job_list, max_processes):
sleep_time = 0.1
processes = list()
for command in job_list:
print 'running {n} processes. Submitting {proc}.'.format(n=len(processes),
proc=str(command))
processes.append(subprocess.Popen(command, shell=False, stdout=None,
stdin=PIPE))
while len(processes) >= max_processes:
time.sleep(sleep_time)
processes = [proc for proc in processes if proc.poll() is None]
while len(processes) > 0:
time.sleep(sleep_time)
processes = [proc for proc in processes if proc.poll() is None]
cmd = '/bin/bash run_what.sh {n}'
job_list = ((cmd.format(n=i)).split() for i in range(100))
submit_job_max_len(job_list, max_processes=50)
你要的是一个线程池。 可以使用固定数量的线程来执行任务。 当没有运行任务时,它会等待任务队列以获取要执行的新代码。
有这个线程池模块 ,但有一条评论说它还没有被认为是完整的。 可能还有其他包,但这是我找到的第一个包。
如果正在运行的系统命令可以使用子进程模块创建流程实例,请根据需要调用它们。 不应该需要线程(它的unpythonic)和多进程这个任务似乎有点过分。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.