Ich brauche eine Warteschlange, in die mehrere Threads Inhalte einfügen können und aus der mehrere Threads lesen können.
Python hat mindestens zwei Warteschlangenklassen, Queue.Queue und collection.deque, wobei die erstere anscheinend die letztere intern verwendet. Beide behaupten, in der Dokumentation threadsicher zu sein.
In den Warteschlangendokumenten heißt es jedoch auch:
collection.deque ist eine alternative Implementierung von unbegrenzten Warteschlangen mit schnellen atomaren append () - und popleft () -Operationen, für die kein Sperren erforderlich ist.
Was ich wohl nicht ganz verstehe: Bedeutet das, dass Deque doch nicht vollständig threadsicher ist?
Wenn ja, kann ich den Unterschied zwischen den beiden Klassen nicht vollständig verstehen. Ich kann sehen, dass die Warteschlange Blockierungsfunktionen hinzufügt. Auf der anderen Seite verliert es einige Deque-Funktionen wie die Unterstützung des In-Operators.
Der direkte Zugriff auf das interne Deque-Objekt ist
x in Queue (). deque
Thread-sicher?
Warum verwendet Queue einen Mutex für seine Operationen, wenn deque bereits threadsicher ist?
quelle
RuntimeError: deque mutated during iteration
ist, was Sie bekommen könnten, ist die Verwendung einesdeque
von mehreren Threads gemeinsam genutzten und ohne Sperren ...deque
während Sie selbst im selben Thread iterieren. Der einzige Grund, warum Sie diesen Fehler nicht erhalten können,Queue
ist, dassQueue
die Iteration nicht unterstützt wird.Antworten:
Queue.Queue
undcollections.deque
dienen verschiedenen Zwecken. Queue.Queue ist dafür vorgesehen, dass verschiedene Threads mithilfe von Nachrichten / Daten in der Warteschlange kommunizieren können, während siecollections.deque
lediglich als Datenstruktur gedacht sind. Warum dasQueue.Queue
hat Methoden wieput_nowait()
,get_nowait()
undjoin()
, währendcollections.deque
dies nicht tut.Queue.Queue
ist nicht dazu gedacht, als Sammlung verwendet zu werden, weshalb es an denin
Betreibern mangelt .Es läuft darauf hinaus: Wenn Sie mehrere Threads haben und möchten, dass diese ohne Sperren kommunizieren können, suchen Sie nach
Queue.Queue
; Wenn Sie nur eine Warteschlange oder eine Warteschlange mit zwei Enden als Datenstruktur möchten, verwenden Siecollections.deque
.Schließlich spielt der Zugriff auf und die Manipulation der internen Deque von a
Queue.Queue
mit dem Feuer - das wollen Sie wirklich nicht.quelle
Queue.Queue
, wird siedeque
unter der Haube verwendet.collections.deque
ist eine Sammlung, währendQueue.Queue
es sich um einen Kommunikationsmechanismus handelt. Der AufwandQueue.Queue
besteht darin, es threadsicher zu machen. Die Verwendungdeque
für die Kommunikation zwischen Threads führt nur zu schmerzhaften Rennen. Wanndeque
immer es threadsicher ist, ist dies ein glücklicher Zufall, wie der Interpreter implementiert wird, und nicht etwas, auf das man sich verlassen kann. DeshalbQueue.Queue
gibt es überhaupt.deque is threadsafe by accident due to the existence of GIL
; Es ist wahr, dass mandeque
sich auf GIL verlässt, um die Thread-Sicherheit zu gewährleisten - aber das ist es nichtby accident
. In der offiziellen Python-Dokumentation heißt es eindeutig, dassdeque
pop*
/append*
Methoden threadsicher sind. Daher muss jede gültige Python-Implementierung dieselbe Garantie bieten (GIL-freie Implementierungen müssen herausfinden, wie dies ohne GIL möglich ist). Auf diese Garantien können Sie sich sicher verlassen.deque
für die Kommunikation verwenden würden. Wenn Sie sichpop
in a einwickelntry/except
, wird eine Besetztschleife enden, die eine enorme Menge an CPU verbraucht und nur auf neue Daten wartet. Dies scheint ein schrecklich ineffizienter Ansatz im Vergleich zu den von angebotenen Blockierungsaufrufen zu seinQueue
, die sicherstellen, dass der auf Daten wartende Thread in den Ruhezustand wechselt und keine CPU-Zeit verschwendet.Queue.Queue
Zeitpunkt lesen , da er wie folgt geschrieben wurdecollections.deque
: hg.python.org/cpython/file/2.7/Lib/Queue.py - Er verwendet Bedingungsvariablen, um den effizientendeque
Zugriff auf die it-Wraps zu ermöglichen sicher und effizient über Fadengrenzen hinweg. Die Erklärung, wie Sie adeque
für die Kommunikation verwenden würden, finden Sie direkt in der Quelle.Wenn Sie nur nach einer thread-sicheren Methode suchen , um Objekte zwischen Threads zu übertragen , funktionieren beide (sowohl für FIFO als auch für LIFO). Für FIFO:
Queue.put()
undQueue.get()
sind threadsicherdeque.append()
unddeque.popleft()
sind threadsicherHinweis:
deque
möglicherweise nicht threadsicher, ich bin mir nicht sicher.deque
blockiert nichtpop()
oderpopleft()
so können Sie Ihren Consumer-Thread-Fluss nicht auf das Blockieren stützen, bis ein neuer Artikel eintrifft.Es scheint jedoch, dass Deque einen signifikanten Effizienzvorteil hat . Hier sind einige Benchmark-Ergebnisse in Sekunden mit CPython 2.7.3 zum Einfügen und Entfernen von 100.000 Elementen
Hier ist der Benchmark-Code:
quelle
deque
möglicherweise nicht threadsicher sind". Woher bekommst du das?Zur Information gibt es ein Python-Ticket, auf das für die Sicherheit von Deque-Threads verwiesen wird ( https://bugs.python.org/issue15329 ). Titel "Klarstellen, welche Deque-Methoden threadsicher sind"
Fazit hier: https://bugs.python.org/issue15329#msg199368
Wie auch immer, wenn Sie nicht 100% sicher sind und Zuverlässigkeit der Leistung vorziehen, setzen Sie einfach ein ähnliches Schloss;)
quelle
Alle Einzelelementmethoden
deque
sind atomar und threadsicher. Alle anderen Methoden sind ebenfalls threadsicher. Solche Dingelen(dq)
,dq[4]
ergeben momentane richtige Werte. Aber denken Sie zB andq.extend(mylist)
: Sie erhalten keine Garantie dafür, dass alle Elemente inmylist
einer Reihe abgelegt werden, wenn andere Threads auch Elemente auf derselben Seite anhängen - dies ist jedoch normalerweise keine Voraussetzung für die Kommunikation zwischen Threads und für die fragliche Aufgabe.A
deque
ist also ~ 20x schneller alsQueue
(was adeque
unter der Haube verwendet) und es sei denn, Sie benötigen nicht die "komfortable" Synchronisations-API (Blockieren / Timeout), die striktemaxsize
Einhaltung oder die "Diese Methoden überschreiben" (_put, _get, .. ) um das Unterklassifizierungsverhalten anderer Warteschlangenorganisationen zu implementieren , oder wenn Sie sich selbst um solche Dinge kümmern, ist ein Baredeque
ein gutes und effizientes Geschäft für die Hochgeschwindigkeitskommunikation zwischen Threads.Tatsächlich ist die häufige Verwendung eines zusätzlichen Mutex und einer zusätzlichen Methode
._get()
usw.Queue.py
auf Abwärtskompatibilitätsbeschränkungen, frühere Überkonstruktionen und mangelnde Sorgfalt bei der Bereitstellung einer effizienten Lösung für dieses wichtige Problem mit Geschwindigkeitsengpässen bei der Kommunikation zwischen Threads zurückzuführen. In älteren Python-Versionen wurde eine Liste verwendet - aber auch list.append () /. Pop (0) war & ist atomar und threadsicher ...quelle
Hinzufügen
notify_all()
zu jedemdeque
append
undpopleft
führt zu weit schlechteren Ergebnissen fürdeque
als die 20x Verbesserung durch Standard erreichtdeque
Verhalten :@ Jonathan ändert seinen Code ein wenig und ich erhalte den Benchmark mit cPython 3.6.2 und füge eine Bedingung in der Deque-Schleife hinzu, um das Verhalten der Warteschlange zu simulieren.
Und es scheint, dass die Leistung durch diese Funktion begrenzt ist
condition.notify_all()
quelle
deque
ist threadsicher. "Vorgänge, für die kein Sperren erforderlich ist" bedeutet, dass Sie das Sperren nicht selbst durchführen müssen, sondern sich darumdeque
kümmern.Wirft man einen Blick auf die
Queue
Quelle, wird der interne deque genanntself.queue
und verwendet ein Mutex für Zugriffs- und Mutationen, soQueue().queue
ist nicht zu verwenden Thread-sicher.Wenn Sie nach einem "In" -Operator suchen, ist eine Deque oder Warteschlange möglicherweise nicht die am besten geeignete Datenstruktur für Ihr Problem.
quelle
(Anscheinend habe ich keinen Ruf zu kommentieren ...) Sie müssen vorsichtig sein, welche Methoden der Deque Sie aus verschiedenen Threads verwenden.
deque.get () scheint threadsicher zu sein, aber ich habe festgestellt, dass dies der Fall ist
kann fehlschlagen, wenn ein anderer Thread gleichzeitig Elemente hinzufügt. Ich habe eine RuntimeException erhalten, die sich über "während der Iteration mutierte Deque" beschwert hat.
Überprüfen Sie collectionsmodule.c, um festzustellen, welche Vorgänge davon betroffen sind
quelle
>>> di = {1:None} >>> for x in di: del di[x]
while
Schleife tun .