Python多处理池:在执行任务期间动态设置进程数

时间:2018-04-12 08:01:53

标签: python multiprocessing

我们在开发机器上提交Python 2.7中的大型CPU密集型作业(由许多独立的并行进程组成),这些作业一次可以持续数天。当这些作业运行大量进程时,机器的响应速度会大大降低。理想情况下,我希望在我们开发代码的过程中限制可用的CPU数量,并尽可能多地运行尽可能多的进程。

Python多处理库允许您指定启动池时的进程数。有没有办法在每次启动新任务时动态更改此数字?

例如,允许20个进程在19-07小时运行,10个进程从07-19小时运行。

一种方法是使用重要的CPU检查活动进程的数量。这就是我希望它的工作方式:

from multiprocessing import Pool
import time 

pool = Pool(processes=20)

def big_task(x):
    while check_n_process(processes=10) is False:
        time.sleep(60*60)
    x += 1
    return x 


x = 1
multiple_results = [pool.apply_async(big_task, (x)) for i in range(1000)]
print([res.get() for res in multiple_results])

但是我需要编写' check_n_process'功能

其他任何想法如何解决这个问题?

(代码需要在Python 2.7中运行 - bash实现是不可行的)。

2 个答案:

答案 0 :(得分:1)

Python multiprocessing.Pool没有提供更改正在运行的Pool的工作量的方法。一个简单的解决方案就是依赖第三方工具。

billiard提供的池用于提供此类功能。

CeleryLuigi等任务队列框架肯定会允许灵活的工作量,但更复杂。

如果使用外部依赖项不可行,您可以尝试以下方法。从this answer开始,您可以根据信号量设置限制机制。

from threading import Semaphore, Lock
from multiprocessing import Pool

def TaskManager(object):
    def __init__(self, pool_size):
        self.pool = Pool(processes=pool_size)
        self.workers = Semaphore(pool_size)
        # ensures the semaphore is not replaced while used
        self.workers_mutex = Lock()  

    def change_pool_size(self, new_size):
        """Set the Pool to a new size."""
        with self.workers_mutex:  
            self.workers = Semaphore(new_size)

    def new_task(self, task):
        """Start a new task, blocks if queue is full."""
        with self.workers_mutex:
            self.workers.acquire()

        self.pool.apply_async(big_task, args=[task], callback=self.task_done))

    def task_done(self):
        """Called once task is done, releases the queue is blocked."""
        with self.workers_mutex:
            self.workers.release()

如果超过X的工作人员忙,池将阻止进一步尝试安排big_tasks。通过控制此机制,您可以限制并发运行的进程数量。当然,这意味着您放弃了Pool排队机制。

task_manager = TaskManager(20)

while True:
    if seven_in_the_morning():
        task_manager.change_pool_size(10)
    if seven_in_the_evening():
        task_manager.change_pool_size(20)

    task = get_new_task()
    task_manager.new_task()  # blocks here if all workers are busy

答案 1 :(得分:0)

这显然是不完整的(也是一个老问题),但是您可以通过跟踪正在运行的进程并在有利时仅调用apply_async()来管理负载。如果每个作业的运行时间都不会长久,则可以通过在工作时间或os.getloadavg()过高时分配较少的作业来降低负载。 我这样做是为了在运行多个“ scp”以规避内部网络的流量整形时管理网络负载(不要告诉任何人!)