Kommunikationsende der Warteschlange

8

Ich lerne, das Queue-Modul zu verwenden, und bin ein wenig verwirrt darüber, wie ein Warteschlangen-Consumer-Thread dazu gebracht werden kann, zu wissen, dass die Warteschlange vollständig ist. Idealerweise möchte ich get() innerhalb des Consumer-Threads verwenden und eine Ausnahme auslösen lassen, wenn die Warteschlange als "erledigt" markiert wurde. Gibt es eine bessere Möglichkeit, dies zu kommunizieren, als durch das Hinzufügen eines Sentinel-Werts zum Markieren des letzten Elements in der Warteschlange?

    
intuited 31.08.2010, 00:21
quelle

5 Antworten

6

Original (das meiste hat sich geändert; siehe Updates unten)

Gestützt auf einige der Vorschläge (danke!) von Glenn Maynard und anderen habe ich beschlossen, einen Nachkomme von Queue.Queue aufzustellen, der eine close -Methode implementiert. Es ist in Form eines primitiven (unverpackten) Moduls verfügbar. Ich werde das ein bisschen aufräumen und es richtig verpacken, wenn ich etwas mehr Zeit habe. Derzeit enthält das Modul nur die Klasse CloseableQueue und die Closed -Ausnahmeklasse. Ich plane, es zu erweitern, um auch Unterklassen von Queue.LifoQueue und Queue.PriorityQueue einzubeziehen.

Es ist momentan in einem ziemlich vorläufigen Zustand, das heißt, obwohl es seine Testsuite besteht, habe ich es noch nicht für irgendwas benutzt. Ihre Laufleistung kann variieren. Ich werde diese Antwort mit spannenden Neuigkeiten auf dem Laufenden halten.

Die CloseableQueue -Klasse unterscheidet sich ein wenig von Glenns Vorschlag dahingehend, dass das Schließen der Warteschlange zukünftige put s verhindert, aber zukünftige get s nicht verhindert, bis die Warteschlange geleert wird. Das machte mir am meisten Sinn; es schien, als ob Funktionalität zum Löschen der Warteschlange als ein separates Mixin * hinzugefügt werden könnte, das orthogonal zur Verschließbarkeitsfunktionalität wäre. Also im Grunde mit CloseableQueue , indem Sie die Warteschlange schließen, geben Sie an, dass das letzte Element put war. Es gibt auch eine Option, dies atomar auszuführen, indem Sie last=True an den letzten put -Aufruf übergeben. Nachfolgende Aufrufe von put und nachfolgende Aufrufe von get , sobald die Warteschlange geleert wurde, sowie ausstehende gesperrte Aufrufe, die diesen Beschreibungen entsprechen, erhöhen die Ausnahme Closed .

Dies ist vor allem in Situationen hilfreich, in denen ein einzelner Produzent Daten für einen oder mehrere Verbraucher generiert, aber es könnte auch für eine Multi-Multi-Anordnung nützlich sein, bei der die Verbraucher auf einen bestimmten Artikel oder eine bestimmte Menge von Artikeln warten. Insbesondere bietet es keine Möglichkeit festzustellen, dass alle Hersteller die Produktion beendet haben. Eine solche Arbeit würde die Bereitstellung eines Weges für die Registrierung von Produzenten ( .open() ?) Sowie eine Möglichkeit bieten, darauf hinzuweisen, dass die Erzeugerregistrierung selbst abgeschlossen ist.

Vorschläge und / oder Code-Reviews sind sehr willkommen. Ich habe nicht viel Concurrency-Code geschrieben, aber hoffentlich ist die Testsuite so gründlich, dass die Tatsache, dass der Code sie übergibt, ein Hinweis auf die Qualität des Codes ist und nicht darauf, dass die Suite dies tut. Ich konnte eine Menge Code aus der Testsuite des Warteschlangenmoduls wiederverwenden: Die Datei selbst ist in diesem Modul enthalten und wird als Basis für verschiedene Unterklassen und Routinen verwendet, einschließlich Regressionstests. Dies hat wahrscheinlich (hoffentlich) dazu beigetragen, eine völlige Unfähigkeit in der Testabteilung zu vermeiden. Der Code selbst überschreibt nur Queue.get und Queue.put mit relativ geringen Änderungen und fügt die Methoden close und closed hinzu.

Ich habe es absichtlich vermieden, irgendeine neugefädelte Phantasie wie Kontextmanager sowohl im Code selbst als auch in der Testsuite zu verwenden, um den Code so rückwärtskompatibel zu halten wie das Queue-Modul selbst, was beträchtlich ist tatsächlich rückwärts. Ich werde wahrscheinlich __enter__ und __exit__ Methoden irgendwann hinzufügen; Andernfalls sollte die Closing -Funktion der ContextLib auf eine CloseableQueue-Instanz angewendet werden können.

*: Hier verwende ich den Begriff "Mixin" lose. Da die Klassen des Queue -Moduls altmodisch sind, müssten Mixins mit Hilfe von Klassenfactory-Funktionen gemischt werden. einige Einschränkungen gelten; Angebot ungültig, wo von Guido verboten.

aktualisieren

Das Modul CloseableQueue bietet jetzt die Klassen CloseableLifoQueue und CloseablePriorityQueue . Ich habe auch einige Komfortfunktionen zur Unterstützung der Iteration hinzugefügt. Muss es noch als ein richtiges Paket nachbearbeiten. Es gibt eine Klassenfactory-Funktion, um eine bequeme Unterklassenbildung für andere Queue.Queue -abgeleitete Klassen zu ermöglichen.

update 2

CloseableQueue ist jetzt über PyPI , z. mit

%Vor%

Kommentare und Kritik sind willkommen, besonders aus dem anonymen Downvoter dieser Antwort.

    
intuited 09.09.2010, 07:47
quelle
8

Warteschlangen haben nicht die Idee, abgeschlossen oder fertig zu sein. Sie können unbegrenzt verwendet werden. Um es zu schließen, wenn Sie fertig sind, müssen Sie tatsächlich keine oder einen anderen magischen Wert am Ende setzen und die Logik schreiben, um danach zu suchen, wie Sie beschrieben haben. Der ideale Weg würde wahrscheinlich das Queue-Objekt bilden.

Siehe Ссылка , um mehr über die Warteschlange im Allgemeinen zu erfahren.

    
Matt Williamson 31.08.2010 00:26
quelle
5

Ein Sentinel ist eine natürliche Möglichkeit, eine Warteschlange zu schließen, aber es gibt ein paar Dinge, auf die man achten sollte.

Denken Sie zunächst daran, dass Sie möglicherweise mehr als einen Verbraucher haben. Daher müssen Sie für jeden aktiven Verbraucher einmal einen Sentinel senden und sicherstellen, dass jeder Verbraucher nur einen Sentinel verbraucht, um sicherzustellen, dass jeder Verbraucher seinen Sentinel zum Herunterfahren erhält / p>

Zweitens: Denken Sie daran, dass die Warteschlange eine Schnittstelle definiert, und dass Code sich, wenn möglich, unabhängig von der zugrunde liegenden Warteschlange verhalten sollte. Möglicherweise verfügen Sie über eine Prioritätsqueue oder Sie verfügen möglicherweise über eine andere Klasse, die dieselbe Schnittstelle verfügbar macht und Werte in einer anderen Reihenfolge zurückgibt.

Leider ist es schwer, mit diesen beiden umzugehen. Um sich mit dem allgemeinen Fall verschiedener Warteschlangen zu befassen, muss ein Verbraucher, der heruntergefahren wird, weiterhin Werte verbrauchen, nachdem er seine Sentinel zum Herunterfahren erhalten hat, bis die Warteschlange leer ist. Das bedeutet, dass es den Sentinel eines anderen Threads verbrauchen kann. Dies ist eine Schwachstelle der Queue-Schnittstelle: Es sollte einen Queue.shutdown -Aufruf haben, damit eine Ausnahme von allen Konsumenten ausgelöst wird, aber das fehlt.

Also, in der Praxis:

  • Wenn Sie sicher sind, dass Sie nur eine reguläre Warteschlange verwenden, senden Sie einfach einen Sentinel pro Thread.
  • Wenn Sie eine PriorityQueue verwenden, stellen Sie sicher, dass das Sentinel die niedrigste Priorität hat.
Glenn Maynard 31.08.2010 00:41
quelle
1

Die Warteschlange ist ein FIFO-Register (first in first out). Denken Sie daran, dass der Verbraucher schneller als der Produzent sein kann. Wenn der Consumer-Thread feststellt, dass die Warteschlange leer ist, führen Sie normalerweise eine der folgenden Aktionen durch:

  1. An API senden: zum nächsten Thread wechseln.
  2. An API senden: Schlafe einige ms und überprüfe dann die Warteschlange erneut.
  3. An API senden: Warte auf ein Ereignis (wie eine neue Nachricht in der Warteschlange).

Wenn Sie nicht möchten, dass der Consumerthread beendet wird, nachdem der Job abgeschlossen ist, wird ein Sentinel-Wert in die Warteschlange gestellt, um die Task zu beenden.

    
GJ. 31.08.2010 08:10
quelle
0

Der beste Weg, dies zu tun, wäre, wenn die Warteschlange selbst einen Client darüber informiert, dass sie den Status 'done' erreicht hat. Der Client kann dann alle geeigneten Maßnahmen ergreifen.

Was Sie vorgeschlagen haben; das Überprüfen der Warteschlange, um zu sehen, ob dies periodisch geschieht, wäre höchst unerwünscht. Polling ist ein Antipattern in der Multithread-Programmierung, Sie sollten immer Benachrichtigungen verwenden.

BEARBEITEN:
Sie sagen also, dass die Warteschlange selbst weiß, dass sie aufgrund bestimmter Kriterien "erledigt" ist, und muss die Kunden darüber informieren. Ich denke, Sie sind richtig und der beste Weg, dies zu tun, ist durch werfen, wenn ein Client ruft () und die Warteschlange ist in den Zustand getan. Wenn Sie dies werfen, würde die Notwendigkeit für einen Sentinel-Wert auf der Client-Seite negieren. Intern kann die Warteschlange erkennen, dass sie in irgendeiner Weise "erledigt" ist, wie es z. Die Warteschlange ist leer, ihr Status wurde auf "Do" gesetzt usw. Ich sehe keinen Bedarf für einen Sentinel-Wert.

    
radman 31.08.2010 00:46
quelle

Tags und Links