使用multiprocessing.Process并发进程数最多


76

我有Python代码:

from multiprocessing import Process

def f(name):
    print 'hello', name

if __name__ == '__main__':
    for i in range(0, MAX_PROCESSES):
        p = Process(target=f, args=(i,))
        p.start()

运行良好。但是,MAX_PROCESSES是变量,可以是1和之间的任何值512。由于我仅在具有8内核的计算机上运行此代码,因此我需要确定是否有可能限制允许同时运行的进程数。我已经调查过了multiprocessing.Queue,但看起来不像我需要的东西-也许我在错误地解释了文档。

有没有一种方法可以限制同时multiprocessing.Process运行的数量?


对于范围(0,min(MAX_PROCESSES,8))中的i:
雅各布

@Jacob我仍然希望所有MAX_PROCESSES运行。为了简单起见,上面的代码被截断了,但是main函数最多被调用512次(因此循环)。所以我想知道是否有一种将进程排队的方法。
Brett 2014年

2
所以您想要一个主/工人设置,并且想要限制工人的数量?
2014年

@Jacob是的,这可能是更好的措辞方式。
Brett 2014年

Answers:


103

可能最明智的做法是multiprocessing.Pool根据系统上可用的最大内核数来生成工作进程池,然后在内核可用时基本上提供任务。

标准文档(http://docs.python.org/2/library/multiprocessing.html#using-a-pool-of-workers)中的示例显示,您还可以手动设置核数:

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    pool = Pool(processes=4)              # start 4 worker processes
    result = pool.apply_async(f, [10])    # evaluate "f(10)" asynchronously
    print result.get(timeout=1)           # prints "100" unless your computer is *very* slow
    print pool.map(f, range(10))          # prints "[0, 1, 4,..., 81]"

而且multiprocessing.cpu_count(),如果代码中需要的话,也很容易知道有一种方法可以计算给定系统上的内核数。

编辑:这是一些看起来适合您的特定情况的代码草案:

import multiprocessing

def f(name):
    print 'hello', name

if __name__ == '__main__':
    pool = multiprocessing.Pool() #use all available cores, otherwise specify the number you want as an argument
    for i in xrange(0, 512):
        pool.apply_async(f, args=(i,))
    pool.close()
    pool.join()

好了,我已经草拟了一个适用于您的特定情况的版本,并已添加到上面的帖子中。
treddy 2014年

49
multiprocessing.cpu_count()-1 or 1在确定要并行运行多少个进程时可能是有用的试探法:-1避免通过独占所有内核来锁定系统,但是如果只有一个CPU可用,则可or以为单核运行提供一个良好的备用。
andybuckley 2014年

如果我的工作繁重且处理很少怎么办?在4核计算机上使用10个线程是否会以某种方式影响程序?
Abhidemon '16

3
请注意,这multiprocessing.cpu_count()不是内核数,而是线程数(在超线程意义上)。
Grismar '19

1
使用上面概述的方法,我能够将应用程序中按计划执行的夜间任务的繁重后端处理时间从大约20分钟减少到大约8分钟。谢谢@treddy!
Fergus

10

我认为Semaphore是您正在寻找的东西,它将在递减为0时阻止主进程。示例代码:

from multiprocessing import Process
from multiprocessing import Semaphore
import time

def f(name, sema):
    print('process {} starting doing business'.format(name))
    # simulate a time-consuming task by sleeping
    time.sleep(5)
    # `release` will add 1 to `sema`, allowing other 
    # processes blocked on it to continue
    sema.release()

if __name__ == '__main__':
    concurrency = 20
    total_task_num = 1000
    sema = Semaphore(concurrency)
    all_processes = []
    for i in range(total_task_num):
        # once 20 processes are running, the following `acquire` call
        # will block the main process since `sema` has been reduced
        # to 0. This loop will continue only after one or more 
        # previously created processes complete.
        sema.acquire()
        p = Process(target=f, args=(i, sema))
        all_processes.append(p)
        p.start()

    # inside main process, wait for all processes to finish
    for p in all_processes:
        p.join()

由于以下代码sema在同一函数中获取和释放,因此其结构更加结构化。但是,如果total_task_num非常大,它将消耗太多资源:

from multiprocessing import Process
from multiprocessing import Semaphore
import time

def f(name, sema):
    print('process {} starting doing business'.format(name))
    # `sema` is acquired and released in the same
    # block of code here, making code more readable,
    # but may lead to problem.
    sema.acquire()
    time.sleep(5)
    sema.release()

if __name__ == '__main__':
    concurrency = 20
    total_task_num = 1000
    sema = Semaphore(concurrency)
    all_processes = []
    for i in range(total_task_num):
        p = Process(target=f, args=(i, sema))
        all_processes.append(p)
        # the following line won't block after 20 processes
        # have been created and running, instead it will carry 
        # on until all 1000 processes are created.
        p.start()

    # inside main process, wait for all processes to finish
    for p in all_processes:
        p.join()

上面的代码将创建total_task_num进程,但是只有concurrency进程将运行,而其他进程被阻止,这将消耗宝贵的系统资源。


这很棒!还解决了Python不能腌制的问题的
PicklingError

我不确定这是否是我做错的事情,但是当在函数f中使用第一个带有释放的代码块但在main中获取代码时,我的sema.release()从未发生。有人遇到过这个问题吗?愚蠢的错误?
user1983682

4

更一般地,它也可能看起来像这样:

import multiprocessing
def chunks(l, n):
    for i in range(0, len(l), n):
        yield l[i:i + n]

numberOfThreads = 4


if __name__ == '__main__':
    jobs = []
    for i, param in enumerate(params):
        p = multiprocessing.Process(target=f, args=(i,param))
        jobs.append(p)
    for i in chunks(jobs,numberOfThreads):
        for j in i:
            j.start()
        for j in i:
            j.join()

当然,这种方式非常残酷(因为它等待垃圾中的每个进程,直到它继续下一个块为止)。在函数调用的运行时间大致相等的情况下,它仍然可以正常工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.