Richtige Verwendung von Mutexen in Python

77

Ich beginne mit Multi-Threads in Python (oder zumindest ist es möglich, dass mein Skript mehrere Threads erstellt). Wäre dieser Algorithmus die richtige Verwendung eines Mutex? Ich habe diesen Code noch nicht getestet und er wird wahrscheinlich nicht einmal funktionieren. Ich möchte nur, dass processData in einem Thread (einzeln) ausgeführt wird und die main while-Schleife weiter ausgeführt wird, selbst wenn sich ein Thread in der Warteschlange befindet.

from threading import Thread
from win32event import CreateMutex
mutex = CreateMutex(None, False, "My Crazy Mutex")
while(1)
    t = Thread(target=self.processData, args=(some_data,))
    t.start()
    mutex.lock()

def processData(self, data)
    while(1)
        if mutex.test() == False:
            do some stuff
            break

Bearbeiten: Wiederlesen meines Codes Ich kann sehen, dass es grob falsch ist. aber hey, deshalb bin ich hier und bitte um Hilfe.

Richard
quelle
Es ist sehr schwierig herauszufinden, was Sie versuchen zu tun. Sie müssen Ihre Absicht genauer erläutern.
Marcelo Cantos
@ Marcelo Cantos, sorry du hast wahrscheinlich recht. Ich möchte, dass mein Code in processData in einem neuen Profil beginnt. Ich möchte, dass jeweils nur ein Thread Daten verarbeiten kann und in welcher Reihenfolge Daten an die Datenverarbeitung gesendet wurden. Ich möchte auch, dass die Haupt-while-Schleife weiter wiederholt wird, während sich andere Threads in der Warteschlange befinden.
Richard
@Richard: Warum möchten Sie Threads verwenden, wenn Sie die gesamte Verarbeitung trotzdem serialisieren möchten? Was ist los mit einer einfachen Schleife? Warum soll der Haupt-Thread weiterhin eine Schleife bilden? Es wird nur die CPU brennen und möglicherweise andere Threads aushungern lassen.
Marcelo Cantos
@Marcelo Cantos, Dies ist nicht mein aktuelles Programm. Mein eigentliches Skript hat über 500 Codezeilen erreicht, darunter Datenbankeinträge und E-Mail. Ich möchte, dass der Hauptthread nur eine serielle Schnittstelle liest, damit die Wahrscheinlichkeit geringer ist, dass der Puffer gefüllt wird, bevor die empfangenen Daten verarbeitet werden. Korrigieren Sie mich, wenn ich falsch liege, aber ich denke, genau hier würde man entweder Threads oder Multiprocessing verwenden
Richard
@Richard: Das ist für den Hauptthread sinnvoll, aber wenn alle anderen Verarbeitungen sequentiell erfolgen sollen, können Sie einfach einen weiteren Thread erstellen, um die gesamte andere Arbeit zu erledigen.
Marcelo Cantos

Antworten:

159

Ich weiß nicht, warum Sie den Mutex des Fensters anstelle des Pythons verwenden. Mit den Python-Methoden ist dies ziemlich einfach:

from threading import Thread, Lock

mutex = Lock()

def processData(data):
    mutex.acquire()
    try:
        print('Do some stuff')
    finally:
        mutex.release()

while True:
    t = Thread(target = processData, args = (some_data,))
    t.start()

Beachten Sie jedoch, dass aufgrund der Architektur von CPython (nämlich der globalen Interpreter-Sperre ) ohnehin immer nur ein Thread gleichzeitig ausgeführt wird. Dies ist in Ordnung, wenn einige von ihnen E / A-gebunden sind, obwohl Sie dies wünschen um die Sperre so weit wie möglich aufzuheben, damit der E / A-gebundene Thread andere Threads nicht daran hindert, ausgeführt zu werden.

Eine Alternative für Python 2.6 und höher ist die Verwendung des Python- multiprocessingPakets. Es spiegelt das threadingPaket wider , erstellt jedoch völlig neue Prozesse, die gleichzeitig ausgeführt werden können. Es ist trivial, Ihr Beispiel zu aktualisieren:

from multiprocessing import Process, Lock

mutex = Lock()

def processData(data):
    with mutex:
        print('Do some stuff')

if __name__ == '__main__':
    while True:
        p = Process(target = processData, args = (some_data,))
        p.start()
Chris B.
quelle
Ich habe Ihren Code ausprobiert und erhalte folgende Fehlermeldung: mit Mutex: SyntaxError: ungültige Syntax. Ich denke, ich könnte try verwenden: außer: In meiner Funktion verwende ich Python 2.4
Richard
6
withist Python 2.5 und multiprocessingPython 2.6. Entsprechend bearbeitet.
Chris B.
1
"Das ist in Ordnung, wenn einige von ihnen E / A-gebunden sind." Eigentlich ist es auch in Ordnung, wenn sie CPU-gebunden sind, solange dies bei Aufrufen von Bibliotheken der Fall ist, die in C und nicht in reinem Python-Code geschrieben sind (z. B. wenn Sie es sind Manipulieren großer Matrizen in Numpy), da die GIL während dieser Aufrufe entsperrt wird.
Arthur Tacca
1
@demented Hedgehog: Das mutexModul ist veraltet. Das Erstellen von Mutexen mit den Modulen threadingund multiprocessing, wie in der Antwort beschrieben, ist nicht veraltet.
Tjalling
Hervorragend geeignet
13

Dies ist die Lösung, die ich mir ausgedacht habe:

import time
from threading import Thread
from threading import Lock

def myfunc(i, mutex):
    mutex.acquire(1)
    time.sleep(1)
    print "Thread: %d" %i
    mutex.release()


mutex = Lock()
for i in range(0,10):
    t = Thread(target=myfunc, args=(i,mutex))
    t.start()
    print "main loop %d" %i

Ausgabe:

main loop 0
main loop 1
main loop 2
main loop 3
main loop 4
main loop 5
main loop 6
main loop 7
main loop 8
main loop 9
Thread: 0
Thread: 1
Thread: 2
Thread: 3
Thread: 4
Thread: 5
Thread: 6
Thread: 7
Thread: 8
Thread: 9
Richard
quelle
17
Es gibt einen möglichen Stillstand. Wenn die print-Anweisung eine Ausnahme auslöst, wird der Mutex niemals freigegeben. Sie müssen verwenden try/releaseoder withsicherstellen, dass die Sperre aufgehoben wird. Siehe meine Antwort.
Chris B.
5
Außerdem muss der Mutex nicht als Argument an die Funktion übergeben werden. Es ist im globalen Bereich verfügbar.
Chris B.
8

Sie müssen Ihren Mutex irgendwann freischalten ...

Guillaume Lebourgeois
quelle
7

Ich möchte die Antwort von chris-b noch ein bisschen verbessern .

Siehe unten für meinen Code:

from threading import Thread, Lock
import threading
mutex = Lock()


def processData(data, thread_safe):
    if thread_safe:
        mutex.acquire()
    try:
        thread_id = threading.get_ident()
        print('\nProcessing data:', data, "ThreadId:", thread_id)
    finally:
        if thread_safe:
            mutex.release()


counter = 0
max_run = 100
thread_safe = False
while True:
    some_data = counter        
    t = Thread(target=processData, args=(some_data, thread_safe))
    t.start()
    counter = counter + 1
    if counter >= max_run:
        break

Wenn Sie thread_safe = Falsein Ihrem ersten Lauf eine while-Schleife festlegen , wird kein Mutex verwendet, und die Threads werden in der folgenden Druckmethode übereinander gesetzt.

Nicht fadensicher

Wenn Sie es jedoch einstellen thread_safe = Trueund ausführen, werden Sie feststellen, dass die gesamte Ausgabe einwandfrei funktioniert.

Gewindesicher

hoffe das hilft.

Teoman Shipahi
quelle