Ich habe kürzlich ein Programm geschrieben, das ein einfaches Erzeuger/Verbraucher-Muster verwendet. Ursprünglich hatte es einen Fehler im Zusammenhang mit der unsachgemäßen Verwendung von threading.Lock, den ich schließlich behoben habe. Aber es hat mich zum Nachdenken gebracht, ob es möglich ist, das Producer/Consumer-Muster ohne Sperren zu implementieren.
Die Anforderungen in meinem Fall waren einfach:
- Ein Hersteller-Thread.
- Ein Verbraucher-Thread.
- Die Warteschlange hat nur Platz für einen Artikel.
- Der Produzent kann den nächsten Gegenstand herstellen, bevor der aktuelle Gegenstand verbraucht ist. Der aktuelle Gegenstand ist also verloren, aber das ist für mich in Ordnung.
- Der Verbraucher kann den aktuellen Artikel verbrauchen, bevor der nächste produziert wird. Der aktuelle Gegenstand wird also zweimal (oder öfter) verbraucht, aber das ist für mich in Ordnung.
Also habe ich dies geschrieben:
QUEUE_ITEM = None
# this is executed in one threading.Thread object
def producer():
global QUEUE_ITEM
while True:
i = produce_item()
QUEUE_ITEM = i
# this is executed in another threading.Thread object
def consumer():
global QUEUE_ITEM
while True:
i = QUEUE_ITEM
consume_item(i)
Meine Frage ist: Ist dieser Code thread-sicher?
Unmittelbarer Kommentar: dieser Code ist nicht wirklich schlosslos - ich benutze CPython und es hat GIL.
Ich habe den Code ein wenig getestet und er scheint zu funktionieren. Er übersetzt einige LOAD- und STORE-Operationen, die aufgrund von GIL atomar sind. Aber ich weiß auch, dass del x
Operation ist nicht atomar, wenn x implementiert __del__
Methode. Wenn mein Artikel also eine __del__
Methode und eine unangenehme Terminplanung passiert, kann es zu Problemen kommen. Oder nicht?
Eine andere Frage ist: Welche Art von Einschränkungen (z. B. für den Typ der produzierten Artikel) muss ich vornehmen, damit der obige Code gut funktioniert?
Meine Fragen beziehen sich nur auf die theoretische Möglichkeit, die Eigenheiten von CPython und GIL auszunutzen, um eine sperrfreie (d.h. keine Sperren wie threading.Lock explizit im Code) Lösung zu finden.