在Python中并行运行多个系统命令
我写了一个简单的脚本,用来对一系列文件执行系统命令。为了加快速度,我想把这些命令并行运行,但不是一次性全部运行——我需要控制同时运行的命令数量的上限。有什么简单的方法可以实现这个呢?
7 个回答
8
你需要把一个信号量对象和线程结合起来。信号量是一个可以限制在某段代码中同时运行的线程数量的对象。在这个例子中,我们会用信号量来限制可以执行os.system调用的线程数量。
首先,我们导入需要的模块:
#!/usr/bin/python
import threading
import os
接下来,我们创建一个信号量对象。这里的数字四表示一次可以有四个线程获取这个信号量。这就限制了可以同时运行的子进程数量。
semaphore = threading.Semaphore(4)
这个函数简单地把对子进程的调用包裹在信号量的调用中。
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秒,因为两组四个的sleep命令是并行运行的。
aw@aw-laptop:~/personal/stackoverflow$ time python 4992400.py
real 0m20.032s
user 0m0.020s
sys 0m0.008s
20
Sven Marnach的回答几乎是对的,但有一个问题。如果最后的max_processes进程中的一个结束了,主程序会尝试启动另一个进程,而这个循环会结束。这会导致主进程关闭,从而可能关闭子进程。对我来说,这种情况发生在使用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()
34
如果你已经在调用子进程了,我觉得没必要使用线程池。下面是一个使用 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])
你需要等待的时间取决于子进程的预期执行时间。