Ich stelle fest, dass häufig empfohlen wird, Warteschlangen mit mehreren Threads anstelle von Listen und zu verwenden .pop()
. Liegt das daran, dass Listen nicht threadsicher sind oder aus einem anderen Grund?
154
Ich stelle fest, dass häufig empfohlen wird, Warteschlangen mit mehreren Threads anstelle von Listen und zu verwenden .pop()
. Liegt das daran, dass Listen nicht threadsicher sind oder aus einem anderen Grund?
Antworten:
Listen selbst sind threadsicher. In CPython schützt die GIL vor gleichzeitigen Zugriffen auf sie, und andere Implementierungen achten darauf, für ihre Listenimplementierungen eine fein abgestimmte Sperre oder einen synchronisierten Datentyp zu verwenden. Obwohl Listen selbst nicht durch Versuche, gleichzeitig darauf zuzugreifen, beschädigt werden können, sind die Daten der Listen nicht geschützt. Beispielsweise:
Es wird nicht garantiert, dass L [0] tatsächlich um eins erhöht wird, wenn ein anderer Thread dasselbe tut, da
+=
es sich nicht um eine atomare Operation handelt. (Sehr, sehr wenige Operationen in Python sind tatsächlich atomar, da die meisten dazu führen können, dass beliebiger Python-Code aufgerufen wird.) Sie sollten Warteschlangen verwenden, da Sie aufgrund der Rasse möglicherweise das falsche Element erhalten oder löschen, wenn Sie nur eine ungeschützte Liste verwenden Bedingungen.quelle
Um einen Punkt in Thomas' ausgezeichneten Antwort zu klären, soll erwähnt werden , dass
append()
ist Thread - sicher.Dies liegt daran, dass keine Bedenken bestehen, dass sich die gelesenen Daten an derselben Stelle befinden, sobald wir sie schreiben . Die
append()
Operation liest keine Daten, sondern schreibt nur Daten in die Liste.quelle
PyList_Append
erfolgt in einer GIL-Sperre. Es wird ein Verweis auf ein Objekt zum Anhängen gegeben. Der Inhalt dieses Objekts kann nach der Auswertung und vor dem Aufruf von geändert werdenPyList_Append
. Aber es wird immer noch dasselbe Objekt sein und sicher angehängt (wenn Sie dies tunlst.append(x); ok = lst[-1] is x
,ok
kann es natürlich falsch sein). Der Code, auf den Sie verweisen, liest nicht aus dem angehängten Objekt, außer um es zu ERHÖHEN. Es liest die angehängte Liste und kann sie neu zuordnen.L[0] += x
ein durchführen wird__getitem__
aufL
und dann__setitem__
aufL
- wennL
unterstützt__iadd__
es die Dinge ein wenig tun wird anders im Objekt - Schnittstelle, aber es gibt immer noch zwei getrennte OperationenL
an der Python - Interpreter Ebene (Sie werden sie in dem sehen kompilierter Bytecode). Dasappend
ist in aa einzigen Methodenaufruf im Bytecode getan.remove
?Hier ist eine umfassende, aber nicht erschöpfende Liste von Beispielen für
list
Vorgänge und ob sie threadsicher sind oder nicht. Hoffnung , eine Antwort in Bezug auf die bekommenobj in a_list
Sprachkonstrukt hier .quelle
Ich hatte kürzlich diesen Fall, in dem ich eine Liste kontinuierlich in einem Thread anhängen, die Elemente durchlaufen und prüfen musste, ob das Element bereit war. In meinem Fall war es ein AsyncResult und es nur dann aus der Liste entfernen, wenn es bereit war. Ich konnte keine Beispiele finden, die mein Problem klar demonstrierten. Hier ist ein Beispiel, das das kontinuierliche Hinzufügen zur Liste in einem Thread und das kontinuierliche Entfernen aus derselben Liste in einem anderen Thread demonstriert. Die fehlerhafte Version läuft problemlos mit kleineren Zahlen, aber halten Sie die Zahlen groß genug und führen Sie a aus einige Male und Sie werden den Fehler sehen
Die FLAWED-Version
Ausgabe bei FEHLER
Version, die Sperren verwendet
Ausgabe
Fazit
Wie in den früheren Antworten erwähnt, ist das Anhängen oder Löschen von Elementen aus der Liste selbst threadsicher. Was nicht threadsicher ist, ist, wenn Sie einen Thread anhängen und einen anderen einfügen
quelle
with r:
) zu verwenden, als explizit aufzurufenr.acquire()
undr.release()