Ein absolut einfaches Beispiel für die Verwendung von Multiprocessing Queue, Pool und Locking


91

Ich habe versucht, die Dokumentation unter http://docs.python.org/dev/library/multiprocessing.html zu lesen , habe aber immer noch Probleme mit der Mehrfachverarbeitung von Warteschlange, Pool und Sperre. Und jetzt konnte ich das folgende Beispiel erstellen.

In Bezug auf Warteschlange und Pool bin ich mir nicht sicher, ob ich das Konzept richtig verstanden habe. Korrigieren Sie mich also, wenn ich falsch liege. Was ich erreichen möchte, ist, 2 Anfragen gleichzeitig zu verarbeiten (Datenliste hat 8 in diesem Beispiel). Was soll ich also verwenden? Pool zum Erstellen von 2 Prozessen, die zwei verschiedene Warteschlangen verarbeiten können (maximal 2), oder sollte ich Queue verwenden, um jedes Mal 2 Eingaben zu verarbeiten? Die Sperre wäre, die Ausgaben korrekt zu drucken.

import multiprocessing
import time

data = (['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
        ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)


def mp_handler(var1):
    for indata in var1:
        p = multiprocessing.Process(target=mp_worker, args=(indata[0], indata[1]))
        p.start()


def mp_worker(inputs, the_time):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

if __name__ == '__main__':
    mp_handler(data)

Antworten:


129

Die beste Lösung für Ihr Problem ist die Verwendung von a Pool. Die Verwendung von Queues und eine separate "Warteschlangen-Feeding" -Funktionalität ist wahrscheinlich übertrieben.

Hier ist eine leicht neu arrangierte Version Ihres Programms, diesmal mit nur 2 Prozessen in einem Pool. Ich glaube, es ist der einfachste Weg, mit minimalen Änderungen am Originalcode:

import multiprocessing
import time

data = (
    ['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
    ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)

def mp_worker((inputs, the_time)):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

def mp_handler():
    p = multiprocessing.Pool(2)
    p.map(mp_worker, data)

if __name__ == '__main__':
    mp_handler()

Beachten Sie, dass die mp_worker()Funktion jetzt ein einzelnes Argument (ein Tupel der beiden vorherigen Argumente) akzeptiert, da diemap() Funktion Ihre Eingabedaten in Unterlisten aufteilt, wobei jede Unterliste Ihrer Worker-Funktion als einzelnes Argument zugewiesen wird.

Ausgabe:

Processs a  Waiting 2 seconds
Processs b  Waiting 4 seconds
Process a   DONE
Processs c  Waiting 6 seconds
Process b   DONE
Processs d  Waiting 8 seconds
Process c   DONE
Processs e  Waiting 1 seconds
Process e   DONE
Processs f  Waiting 3 seconds
Process d   DONE
Processs g  Waiting 5 seconds
Process f   DONE
Processs h  Waiting 7 seconds
Process g   DONE
Process h   DONE

Bearbeiten Sie gemäß @Thales Kommentar unten:

Wenn Sie "eine Sperre für jedes Poollimit" wünschen, damit Ihre Prozesse in Tandempaaren ausgeführt werden, gilt Folgendes:

A warten B warten | A erledigt, B erledigt | C warten, D warten | C erledigt, D erledigt | ...

Ändern Sie dann die Handlerfunktion, um Pools (von 2 Prozessen) für jedes Datenpaar zu starten:

def mp_handler():
    subdata = zip(data[0::2], data[1::2])
    for task1, task2 in subdata:
        p = multiprocessing.Pool(2)
        p.map(mp_worker, (task1, task2))

Jetzt ist Ihre Ausgabe:

 Processs a Waiting 2 seconds
 Processs b Waiting 4 seconds
 Process a  DONE
 Process b  DONE
 Processs c Waiting 6 seconds
 Processs d Waiting 8 seconds
 Process c  DONE
 Process d  DONE
 Processs e Waiting 1 seconds
 Processs f Waiting 3 seconds
 Process e  DONE
 Process f  DONE
 Processs g Waiting 5 seconds
 Processs h Waiting 7 seconds
 Process g  DONE
 Process h  DONE

Vielen Dank für das einfache und direkte Beispiel, wie es geht. Aber wie kann ich die Sperre für jedes Poollimit anwenden? Ich meine, wenn Sie den Code ausführen, würde ich gerne etwas sehen wie "A wartet B wartet | A erledigt, b erledigt | C wartet, D wartet | C erledigt, D erledigt"
2.

2
Mit anderen Worten, Sie möchten nicht, dass C beginnt, bis sowohl A als auch B fertig sind?
Velimir Mlaker

Genau, ich kann es mit Multiprocessing machen.
Prozess,

Vielen Dank, arbeiten Sie wie beabsichtigt, aber auf Funktion mp_handler verweisen Sie auf die variablen Daten anstelle von var1 :)
thclpr

Okay, danke, ich habe var1alles entfernt und mich datastattdessen auf global bezogen .
Velimir Mlaker

8

Dies hängt möglicherweise nicht zu 100% mit der Frage zusammen, aber bei meiner Suche nach einem Beispiel für die Verwendung von Multiprocessing mit einer Warteschlange wird dies zuerst bei Google angezeigt.

Dies ist eine grundlegende Beispielklasse, mit der Sie Elemente instanziieren und in eine Warteschlange stellen und warten können, bis die Warteschlange beendet ist. Das ist alles was ich brauchte.

from multiprocessing import JoinableQueue
from multiprocessing.context import Process


class Renderer:
    queue = None

    def __init__(self, nb_workers=2):
        self.queue = JoinableQueue()
        self.processes = [Process(target=self.upload) for i in range(nb_workers)]
        for p in self.processes:
            p.start()

    def render(self, item):
        self.queue.put(item)

    def upload(self):
        while True:
            item = self.queue.get()
            if item is None:
                break

            # process your item here

            self.queue.task_done()

    def terminate(self):
        """ wait until queue is empty and terminate processes """
        self.queue.join()
        for p in self.processes:
            p.terminate()

r = Renderer()
r.render(item1)
r.render(item2)
r.terminate()

2
Was sind item1und item2? Handelt es sich um eine Aufgabe oder Funktionen, die in zwei verschiedenen Prozessen ausgeführt werden?
Zelphir Kaltstahl

2
Ja, es handelt sich um Aufgaben oder Eingabeparameter, die parallel verarbeitet werden.
Linqu

8

Hier ist mein persönlicher Beitrag zu diesem Thema:

Gist hier (Pull-Anfragen willkommen!): Https://gist.github.com/thorsummoner/b5b1dfcff7e7fdd334ec

import multiprocessing
import sys

THREADS = 3

# Used to prevent multiple threads from mixing thier output
GLOBALLOCK = multiprocessing.Lock()


def func_worker(args):
    """This function will be called by each thread.
    This function can not be a class method.
    """
    # Expand list of args into named args.
    str1, str2 = args
    del args

    # Work
    # ...



    # Serial-only Portion
    GLOBALLOCK.acquire()
    print(str1)
    print(str2)
    GLOBALLOCK.release()


def main(argp=None):
    """Multiprocessing Spawn Example
    """
    # Create the number of threads you want
    pool = multiprocessing.Pool(THREADS)

    # Define two jobs, each with two args.
    func_args = [
        ('Hello', 'World',), 
        ('Goodbye', 'World',), 
    ]


    try:
        # Spawn up to 9999999 jobs, I think this is the maximum possible.
        # I do not know what happens if you exceed this.
        pool.map_async(func_worker, func_args).get(9999999)
    except KeyboardInterrupt:
        # Allow ^C to interrupt from any thread.
        sys.stdout.write('\033[0m')
        sys.stdout.write('User Interupt\n')
    pool.close()

if __name__ == '__main__':
    main()

1
Ich bin mir nicht ganz sicher, ob .map_async () in irgendeiner Weise besser als .map () ist.
ThorSummoner

3
Das Argument für get()eine Zeitüberschreitung hat nichts mit der Anzahl der gestarteten Jobs zu tun.
Mata

@mata also, soll das in einer Polling-Schleife verwendet werden? .get(timeout=1)? und ist es okay zu sagen .get(), um die fertige Liste zu bekommen?
ThorSummoner

Ja, .get()wartet auf unbestimmte Zeit, bis alle Ergebnisse verfügbar sind, und gibt die Ergebnisliste zurück. Sie können eine Abfrageschleife verwenden, um zu überprüfen, ob Wetterergebnisse verfügbar sind, oder Sie können eine Rückruffunktion im map_async()Anruf übergeben, die dann für jedes Ergebnis aufgerufen wird, sobald es verfügbar ist.
Mata

2

Für alle, die Editoren wie Komodo Edit (win10) verwenden, fügen Sie Folgendes hinzu sys.stdout.flush():

def mp_worker((inputs, the_time)):
    print " Process %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs
    sys.stdout.flush()

oder als erste Zeile an:

    if __name__ == '__main__':
       sys.stdout.flush()

Auf diese Weise können Sie sehen, was während der Ausführung des Skripts geschieht. anstatt auf das schwarze Kommandozeilenfeld schauen zu müssen.


1

Hier ist ein Beispiel aus meinem Code (für Thread-Pool, aber ändern Sie einfach den Klassennamen und Sie haben einen Prozesspool):

def execute_run(rp): 
   ... do something 

pool = ThreadPoolExecutor(6)
for mat in TESTED_MATERIAL:
    for en in TESTED_ENERGIES:
        for ecut in TESTED_E_CUT:
            rp = RunParams(
                simulations, DEST_DIR,
                PARTICLE, mat, 960, 0.125, ecut, en
            )
            pool.submit(execute_run, rp)
pool.join()

Grundsätzlich:

  • pool = ThreadPoolExecutor(6) Erstellt einen Pool für 6 Threads
  • Dann haben Sie eine Reihe von For's, die dem Pool Aufgaben hinzufügen
  • pool.submit(execute_run, rp) Fügt dem Pool eine Aufgabe hinzu. Das erste arogument ist eine Funktion, die in einem Thread / Prozess aufgerufen wird. Die restlichen Argumente werden an die aufgerufene Funktion übergeben.
  • pool.join wartet, bis alle Aufgaben erledigt sind.

2
Beachten Sie, dass Sie verwenden concurrent.futures, aber das OP fragt nach multiprocessingund Python 2.7.
Tim Peters
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.