it-swarm.com.de

Python teilt eine Sperre zwischen Prozessen

Ich versuche, eine partielle Funktion zu verwenden, damit pool.map () eine Funktion ansprechen kann, die mehr als einen Parameter hat (in diesem Fall ein Lock () - Objekt).

Hier ist ein Beispielcode (aus einer Antwort auf eine meiner vorherigen Fragen):

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()

Wenn ich diesen Code jedoch ausführe, erhalte ich die Fehlermeldung:

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

Was fehlt mir hier? Wie kann ich die Sperre zwischen meinen Unterprozessen freigeben?

36
DJMcCarthy12

Entschuldigung, ich hätte dies in meiner Antwort auf Ihre andere Frage erfahren sollen. Sie können keine normalen multiprocessing.Lock-Objekte an Pool-Methoden übergeben, da diese nicht ausgewählt werden können. Es gibt zwei Möglichkeiten, dies zu umgehen. Eine ist das Erstellen von Manager() und das Übergeben einer 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()

Dies ist jedoch ein bisschen Schwergewicht. Die Verwendung einer Manager erfordert das Erzeugen eines anderen Prozesses zum Hosten des Manager-Servers. Alle Aufrufe an acquire/release der Sperre müssen über IPC an diesen Server gesendet werden.

Die andere Option besteht darin, die reguläre multiprocessing.Lock() zur Poolerstellungszeit mit der initializer kwarg zu übergeben. Dadurch wird Ihre Sperrinstanz in allen untergeordneten Workern global:

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()

Die zweite Lösung hat den Nebeneffekt, dass partial nicht mehr erforderlich ist.

67
dano

Hier ist eine Version (mit Barrier anstelle von Lock, aber Sie haben die Idee), die auch unter Windows funktionieren würde (wo die fehlende fork zusätzliche Probleme verursacht):

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