web-dev-qa-db-ja.com

Pythonでmultiprocessing.poolを使用してグローバルロック/セマフォを作成するにはどうすればよいですか?

子プロセスでのリソースアクセスを制限したい。たとえば、-limit http downloadsdisk ioなどです。この基本コードを拡張してどのように実現できますか?

基本的なコード例をいくつか教えてください。

pool = multiprocessing.Pool(multiprocessing.cpu_count())
while job_queue.is_jobs_for_processing():
  for job in job_queue.pull_jobs_for_processing:
    pool.apply_async(do_job, callback = callback)
pool.close()
pool.join()
16
Chameleon

すべての子プロセスでグローバルを定義するために、プールを作成するときに、initializerおよびinitargs引数を使用します。

例えば:

from multiprocessing import Pool, Lock
from time import sleep

def do_job(i):
    "The greater i is, the shorter the function waits before returning."
    with lock:
        sleep(1-(i/10.))
        return i

def init_child(lock_):
    global lock
    lock = lock_

def main():
    lock = Lock()
    poolsize = 4
    with Pool(poolsize, initializer=init_child, initargs=(lock,)) as pool:
        results = pool.imap_unordered(do_job, range(poolsize))
        print(list(results))

if __name__ == "__main__":
    main()

このコードは、ロックを使用しているため、0〜3の数字を昇順(ジョブが送信された順序)で出力します。 with lock:行をコメントアウトして、番号が降順で出力されることを確認します。

このソリューションは、WindowsとUNIXの両方で機能します。ただし、プロセスはunixシステムでフォークできるため、unixはモジュールスコープでグローバル変数を宣言するだけで済みます。子プロセスは、親のメモリのコピーを取得します。これには、引き続き機能するロックオブジェクトが含まれます。したがって、初期化子は厳密には必要ありませんが、コードがどのように機能するかを文書化するのに役立ちます。マルチプロセッシングがフォークによってプロセスを作成できる場合、以下も機能します。

from multiprocessing import Pool, Lock
from time import sleep

lock = Lock()

def do_job(i):
    "The greater i is, the shorter the function waits before returning."
    with lock:
        sleep(1-(i/10.))
        return i

def main():
    poolsize = 4
    with Pool(poolsize) as pool:
        results = pool.imap_unordered(do_job, range(poolsize))
        print(list(results))

if __name__ == "__main__":
    main()
24
Dunes

グローバルセマフォを使用して、リソースにアクセスしている場合はそれを取得します。例えば:

import multiprocessing
from time import sleep

semaphore = multiprocessing.Semaphore(2)

def do_job(id):
    with semaphore:
        sleep(1)
    print("Finished job")

def main():
    pool = multiprocessing.Pool(6)
    for job_id in range(6):
        print("Starting job")
        pool.apply_async(do_job, [job_id])
    pool.close()
    pool.join()

if __name__ == "__main__":
    main()

他のスレッドがセマフォを待機しているため、このプログラムは毎秒2つのジョブのみを終了します。

2
MarcelSimon