我想同时运行program.py的多个实例,同时限制同时运行的实例数(例如,我的系统上可用的CPU内核数)。例如,如果我有10个内核并且总共需要运行1000次program.py,那么在任何给定时间只会创建并运行10个实例。
我已经尝试过使用多处理模块,多线程和使用队列,但在我看来,没有任何东西可以用来实现简单的实现。我遇到的最大问题是找到一种方法来限制同时运行的进程数。这很重要,因为如果我一次创建1000个进程,它就相当于一个fork炸弹。我不需要以编程方式从进程返回的结果(它们输出到磁盘),并且所有进程都彼此独立地运行。
任何人都可以请给我建议或如何在python中实现这一点,甚至bash?我发布到目前为止我使用队列编写的代码,但它没有按预期工作,可能已经走错了路。
非常感谢。
答案 0 :(得分:21)
我知道你提到过Pool.map方法对你没有多大意义。地图只是一种简单的方法,可以为它提供工作源,并且可以调用它来应用于每个项目。地图的func
可以是在给定arg上执行实际工作的任何入口点。
如果这对你来说不合适,我在这里有一个关于使用Producer-Consumer模式的非常详细的答案:https://stackoverflow.com/a/11196615/496445
基本上,您创建一个队列,并启动N个工作人员。然后,您可以从主线程提供队列,也可以创建一个为队列提供信息的Producer进程。工作人员只是从队列中继续工作,并且永远不会有更多的并发工作发生,而不是你已经开始的进程数。
您还可以选择对队列设置限制,以便在已经有太多未完成的工作时阻止生产者,如果您还需要对生产者消耗的速度和资源设置约束。
被调用的工作函数可以执行任何操作。这可以是一些系统命令的包装,或者它可以导入你的python lib并运行主例程。有一些特定的进程管理系统可以让你设置配置来在有限的资源下运行你的任意可执行文件,但这只是一个基本的python方法。
我other answer的片段:
基本资讯池:
from multiprocessing import Pool
def do_work(val):
# could instantiate some other library class,
# call out to the file system,
# or do something simple right here.
return "FOO: %s" % val
pool = Pool(4)
work = get_work_args()
results = pool.map(do_work, work)
使用流程管理器和生产者
from multiprocessing import Process, Manager
import time
import itertools
def do_work(in_queue, out_list):
while True:
item = in_queue.get()
# exit signal
if item == None:
return
# fake work
time.sleep(.5)
result = item
out_list.append(result)
if __name__ == "__main__":
num_workers = 4
manager = Manager()
results = manager.list()
work = manager.Queue(num_workers)
# start for workers
pool = []
for i in xrange(num_workers):
p = Process(target=do_work, args=(work, results))
p.start()
pool.append(p)
# produce data
# this could also be started in a producer process
# instead of blocking
iters = itertools.chain(get_work_args(), (None,)*num_workers)
for item in iters:
work.put(item)
for p in pool:
p.join()
print results
答案 1 :(得分:3)
您应该使用流程主管。一种方法是使用Circus提供的API以“编程方式”执行该操作,文档站点现在处于脱机状态,但我认为这只是一个临时问题,无论如何,您可以使用Circus来处理此问题。另一种方法是使用supervisord并将流程的参数numprocs
设置为您拥有的核心数。
使用马戏团的一个例子:
from circus import get_arbiter
arbiter = get_arbiter("myprogram", numprocesses=3)
try:
arbiter.start()
finally:
arbiter.stop()
答案 2 :(得分:2)
Bash脚本而不是Python,但我经常使用它进行简单的并行处理:
#!/usr/bin/env bash
waitForNProcs()
{
nprocs=$(pgrep -f $procName | wc -l)
while [ $nprocs -gt $MAXPROCS ]; do
sleep $SLEEPTIME
nprocs=$(pgrep -f $procName | wc -l)
done
}
SLEEPTIME=3
MAXPROCS=10
procName=myPython.py
for file in ./data/*.txt; do
waitForNProcs
./$procName $file &
done
或者对于非常简单的情况,另一个选项是xargs,其中P设置了procs的数量
find ./data/ | grep txt | xargs -P10 -I SUB ./myPython.py SUB
答案 3 :(得分:0)
虽然有许多关于使用multiprocessing.pool的答案,但关于如何使用multiprocessing.Process的代码片段并不多,这在内存使用很重要时确实更有用。启动1000个进程会使CPU过载并终止内存。如果每个进程及其数据管道都是内存密集型的,那么OS或Python本身将限制并行进程的数量。我开发了以下代码来限制批量提交给CPU的同时作业数。批量大小可以与CPU核心数成比例。在我的Windows PC中,每批次的作业数量可以高达CPU可用数量的4倍。
import multiprocessing
def func_to_be_multiprocessed(q,data):
q.put(('s'))
q = multiprocessing.Queue()
worker = []
for p in range(number_of_jobs):
worker[p].append(multiprocessing.Process(target=func_to_be_multiprocessed, \
args=(q,data)...))
num_cores = multiprocessing.cpu_count()
Scaling_factor_batch_jobs = 3.0
num_jobs_per_batch = num_cores * Scaling_factor_batch_jobs
num_of_batches = number_of_jobs // num_jobs_per_batch
for i_batch in range(num_of_batches):
floor_job = i_batch * num_jobs_per_batch
ceil_job = floor_job + num_jobs_per_batch
for p in worker[floor_job : ceil_job]:
worker.start()
for p in worker[floor_job : ceil_job]:
worker.join()
for p in worker[ceil_job :]:
worker.start()
for p in worker[ceil_job :]:
worker.join()
for p in multiprocessing.active_children():
p.terminate()
result = []
for p in worker:
result.append(q.get())
唯一的问题是,如果任何批次中的任何作业无法完成并导致挂起情况,则不会启动其余批次作业。因此,要处理的函数必须具有适当的错误处理例程。