簡體   English   中英

Python中的多處理,同時限制正在運行的進程數

[英]Multiprocessing in Python while limiting the number of running processes

我想同時運行program.py的多個實例,同時限制同時運行的實例數(例如,我的系統上可用的CPU內核數)。 例如,如果我有10個內核並且總共需要運行1000次program.py,那么在任何給定時間只會創建並運行10個實例。

我已經嘗試過使用多處理模塊,多線程和使用隊列,但在我看來似乎沒有任何東西能夠實現簡單的實現。 我遇到的最大問題是找到一種方法來限制同時運行的進程數。 這很重要,因為如果我一次創建1000個進程,它就相當於一個fork炸彈。 我不需要以編程方式從進程返回的結果(它們輸出到磁盤),並且所有進程都彼此獨立地運行。

任何人都可以請給我建議或如何在python中實現這一點,甚至bash? 我發布到目前為止我使用隊列編寫的代碼,但它沒有按預期工作,可能已經走錯了路。

非常感謝。

我知道你提到過Pool.map方法對你沒有多大意義。 地圖只是一種簡單的方法,可以為它提供工作源,並且可以調用它來應用於每個項目。 地圖的func可以是在給定arg上執行實際工作的任何入口點。

如果這對您來說不合適,我在這里有一個關於使用Producer-Consumer模式的非常詳細的答案: https//stackoverflow.com/a/11196615/496445

基本上,您創建一個隊列,並啟動N個工作人員。 然后,您可以從主線程提供隊列,也可以創建一個為隊列提供信息的Producer進程。 工作人員只是繼續從隊列中獲取工作,並且永遠不會發生比您已啟動的進程數更多的並發工作。

您還可以選擇對隊列設置限制,以便在已經有太多未完成的工作時阻塞生產者,如果您還需要對生產者消耗的速度和資源施加約束。

被調用的工作函數可以做任何你想做的事情。 這可以是一些系統命令的包裝,或者它可以導入你的python lib並運行主例程。 有一些特定的進程管理系統可以讓你設置configs來在有限的資源下運行你的任意可執行文件,但這只是一個基本的python方法。

我的其他答案的片段:

基本池:

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

您應該使用流程主管。 一種方法是使用Circus提供的API以“編程方式”執行此操作,文檔站點現在處於脫機狀態,但我認為這只是一個臨時問題,無論如何,您可以使用Circus來處理此問題。 另一種方法是使用supervisord並將進程的參數numprocs設置為您擁有的核心數。

使用Circus的一個例子:

from circus import get_arbiter

arbiter = get_arbiter("myprogram", numprocesses=3)
try:
    arbiter.start()
finally:
    arbiter.stop()

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設置過程的數量

find ./data/ | grep txt | xargs -P10 -I SUB ./myPython.py SUB 

雖然有很多關於使用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())

唯一的問題是,如果任何批次中的任何作業無法完成並導致掛起情況,則不會啟動其余批次作業。 因此,要處理的函數必須具有適當的錯誤處理例程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM