Suche…


Bemerkungen

Die Python-Entwickler stellten sicher, dass die API zwischen threading und multiprocessing ähnlich ist, sodass der Wechsel zwischen den beiden Varianten für Programmierer einfacher ist.

Das Einfädelmodul

from __future__ import print_function
import threading
def counter(count):
    while count > 0:
        print("Count value", count)
        count -= 1
    return

t1 = threading.Thread(target=countdown,args=(10,))
t1.start()
t2 = threading.Thread(target=countdown,args=(20,))
t2.start()

Bei bestimmten Implementierungen von Python wie CPython ist wahr Parallelität nicht wegen der Verwendung , was als die GIL bekannt ist die Verwendung von Threads erreicht, oder G LOBALE I nterpreter L ock.

Hier ist ein hervorragender Überblick über die Parallelität von Python:

Python-Parallelität von David Beazley (YouTube)

Das Multiprocessing-Modul

from __future__ import print_function
import multiprocessing


def countdown(count):
    while count > 0:
        print("Count value", count)
        count -= 1
    return

if __name__ == "__main__":
    p1 = multiprocessing.Process(target=countdown, args=(10,))
    p1.start()

    p2 = multiprocessing.Process(target=countdown, args=(20,))
    p2.start()

    p1.join()
    p2.join()

Hier wird jede Funktion in einem neuen Prozess ausgeführt. Da eine neue Instanz von Python VM den Code GIL , gibt es keine GIL und Sie können Parallelität auf mehreren Kernen ausführen.

Die Process.start Methode startet diesen neuen Prozess und führen Sie die Funktion in dem übergebenen target Argumente mit den Argumenten args . Die Process.join Methode wartet auf das Ende der Ausführung der Prozesse p1 und p2 .

Die neuen Prozesse werden gestartet unterschiedlich , je nach Version von Python und der entsprechenden Plattform , auf die der Code zum Beispiel ausgeführt wird :

  • Windows verwendet spawn , um den neuen Prozess zu erstellen.
  • Bei Unix-Systemen und Versionen vor 3.3 werden die Prozesse mithilfe einer fork .
    Beachten Sie, dass diese Methode die POSIX-Verwendung von Fork nicht beachtet und daher zu unerwartetem Verhalten führt, insbesondere wenn Sie mit anderen Multiprocessing-Bibliotheken interagieren.
  • Mit Unix-System und Version 3.4 und höher können Sie die neuen Prozesse entweder mit fork , forkserver oder spawn Verwendung von multiprocessing.set_start_method am Anfang Ihres Programms starten. forkserver und spawn methoden sind langsamer als forking, vermeiden jedoch unerwartetes Verhalten.

POSIX-Gabelverwendung :

Nach einer Abzweigung in einem Multithread-Programm kann das Kind nur sicherheitsgerichtete asynchrone Funktionen aufrufen, bis es ausgeführt wird.
( siehe )

Mit Fork wird ein neuer Prozess mit demselben Status für den gesamten aktuellen Mutex gestartet, aber nur der MainThread wird gestartet. Dies ist unsicher, da dies zu Rennbedingungen führen kann, z .

  • Wenn Sie eine Lock in MainThread und sie an einen anderen Thread übergeben, der sie an einem bestimmten Punkt sperren soll. Wenn die fork gleichzeitig occures, wird das neue Verfahren mit einem gesperrten Schloss beginnen, die nie als der zweite Thread freigegeben wird nicht in diesem neuen Verfahren existiert.

Eigentlich sollte diese Art von Verhalten nicht in reinem Python auftreten, da multiprocessing dies richtig handhabt. Wenn Sie jedoch mit anderen Bibliotheken interagieren, kann diese Art von Verhalten auftreten, was zum Absturz Ihres Systems führen kann (z. B. mit numpy / beschleunigt auf macOS).

Übergabe von Daten zwischen Multiprozessoren

Da die Daten zwischen zwei Threads vertraulich behandelt werden (wenn man annimmt, dass gleichzeitiges Lesen und gleichzeitiges Schreiben Konflikte verursachen können, was zu Race-Bedingungen führt), wurde eine Reihe von eindeutigen Objekten erstellt, um das Weiterleiten von Daten zwischen Threads zu erleichtern. Jede wirklich atomare Operation kann zwischen Threads verwendet werden, aber es ist immer sicher, bei Queue zu bleiben.

import multiprocessing
import queue
my_Queue=multiprocessing.Queue() 
#Creates a queue with an undefined maximum size
#this can be dangerous as the queue becomes increasingly large
#it will take a long time to copy data to/from each read/write thread

Die meisten Leute schlagen vor, bei der Verwendung der Warteschlange immer die Warteschlangendaten in einem try: zu platzieren, außer: blockieren statt leer zu verwenden. Bei Anwendungen, bei denen es nicht darauf ankommt, einen Scan-Zyklus zu überspringen (Daten können in die Warteschlange gestellt werden, während der queue.Empty==True aus der Warteschlange queue.Empty==True in die Warteschlange. queue.Empty==False ), ist es in der Regel besser, read zu platzieren und Schreibzugriff auf das, was ich als Iftry-Block bezeichne, da eine 'if'-Anweisung technisch performanter ist als die Ausnahme.

import multiprocessing
import queue
'''Import necessary Python standard libraries, multiprocessing for classes and queue for the queue exceptions it provides'''
def Queue_Iftry_Get(get_queue, default=None, use_default=False, func=None, use_func=False):
    '''This global method for the Iftry block is provided for it's reuse and 
standard functionality, the if also saves on performance as opposed to catching
 the exception, which is expencive.
        It also allows the user to specify a function for the outgoing data to use,
 and a default value to return if the function cannot return the value from the queue'''
        if get_queue.empty():
            if use_default:
                return default
        else:
            try:
                value = get_queue.get_nowait()
            except queue.Empty:
                if use_default:
                    return default
            else:
                if use_func:
                    return func(value)
                else:
                    return value
    def Queue_Iftry_Put(put_queue, value):
        '''This global method for the Iftry block is provided because of its reuse 
and 
standard functionality, the If also saves on performance as opposed to catching
 the exception, which is expensive.
        Return True if placing value in the queue was successful. Otherwise, false'''
        if put_queue.full():
            return False
        else:
            try:
                put_queue.put_nowait(value)
            except queue.Full:
                return False
            else:
                return True


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow