6 Stimmen

Ist dieser Python Producer-Consumer Lockless-Ansatz thread-sicher?

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.

0voto

Paweł Polewicz Punkte 3591

El __del__ könnte ein Problem sein, wie Sie sagten. Es könnte vermieden werden, wenn es nur eine Möglichkeit gäbe, den Garbage Collector daran zu hindern, die __del__ Methode für das alte Objekt, bevor wir das neue Objekt dem QUEUE_ITEM . Wir bräuchten etwas wie:

increase the reference counter on the old object
assign a new one to `QUEUE_ITEM`
decrease the reference counter on the old object

Ich weiß aber leider nicht, ob das möglich ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X