web-dev-qa-db-ja.com

Pythonプロセス間でロックを共有する

Pool.map()が複数のパラメーターを持つ関数(この場合はLock()オブジェクト)をターゲットにできるように、部分的な関数を使用しようとしています。

以下にコードの例を示します(私の前の質問への回答から引用)。

from functools import partial

def target(lock, iterable_item):
    for item in items:
        # Do cool stuff
        if (... some condition here ...):
            lock.acquire()
            # Write to stdout or logfile, etc.
            lock.release()

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    l = multiprocessing.Lock()
    func = partial(target, l)
    pool.map(func, iterable)
    pool.close()
    pool.join()

ただし、このコードを実行すると、エラーが発生します。

Runtime Error: Lock objects should only be shared between processes through inheritance.

ここで何が欠けていますか?サブプロセス間でロックを共有するにはどうすればよいですか?

39
DJMcCarthy12

申し訳ありませんが、他の質問への回答でこれを把握する必要がありました。通常の_multiprocessing.Lock_オブジェクトをPoolメソッドに渡すことはできません。それらはピクルできないためです。これを回避するには2つの方法があります。 1つは、 Manager() を作成し、 Manager.Lock() を渡すことです。

_def main():
    iterable = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool()
    m = multiprocessing.Manager()
    l = m.Lock()
    func = partial(target, l)
    pool.map(func, iterable)
    pool.close()
    pool.join()
_

ただし、これは少し重いです。 Managerを使用するには、Managerサーバーをホストする別のプロセスを生成する必要があります。そして、acquire/releaseへのすべての呼び出しは、IPCを介してそのサーバーに送信する必要があります。

他のオプションは、initializer kwargを使用して、プールの作成時に通常のmultiprocessing.Lock()を渡すことです。これにより、すべての子ワーカーでロックインスタンスがグローバルになります。

_def target(iterable_item):
    for item in items:
        # Do cool stuff
        if (... some condition here ...):
            lock.acquire()
            # Write to stdout or logfile, etc.
            lock.release()
def init(l):
    global lock
    lock = l

def main():
    iterable = [1, 2, 3, 4, 5]
    l = multiprocessing.Lock()
    pool = multiprocessing.Pool(initializer=init, initargs=(l,))
    pool.map(target, iterable)
    pool.close()
    pool.join()
_

2番目のソリューションには、partialが不要になるという副作用があります。

76
dano

Windowsでも動作するバージョン(Barrierの代わりにLockを使用しますが、アイデアは得られます)(欠落しているforkが追加のトラブルを引き起こしている):

import multiprocessing as mp

def procs(uid_barrier):
    uid, barrier = uid_barrier
    print(uid, 'waiting')
    barrier.wait()
    print(uid, 'past barrier')    

def main():
    N_PROCS = 10
    with mp.Manager() as man:
        barrier = man.Barrier(N_PROCS)
        with mp.Pool(N_PROCS) as p:
            p.map(procs, ((uid, barrier) for uid in range(N_PROCS)))

if __== '__main__':
    mp.freeze_support()
    main()
0
Tom Pohl