Sind STL container Thread sicher?
-
Ohne es genau zu wissen, würde ich sagen, das die einzelnen Methoden wahrscheinlich Threadsave sind. Das heißt aber nicht, das du einfach irgendwo einfügen und wo anderst lesen kannst. Dazu müssten ja alle Iteratoren und was man sich sonst wo von den containern holt, bei einer änderung upgedatet werden. Und so eine Art von "Threadsafe" wird es wohl bei keinem container geben.
-
BorisDieKlinge schrieb:
ich benutze die std::list in zwei thread, wobei einer der thread nur elemente liest, der andere fügt elemente hinzu.. nun die frage ob std::list allgemein trheadsicher ist?
In der Regel nein. Genaueres zur Threadsicherheit musst du aber zu der konkreten STL-Implementierung suchen, die haben teils starke Unterschiede (Ist ein Implementierungsdetail).
cu André
-
wettermann schrieb:
Dazu müssten ja alle Iteratoren und was man sich sonst wo von den containern holt, bei einer änderung upgedatet werden. Und so eine Art von "Threadsafe" wird es wohl bei keinem container geben.
std::list garantiert, dass iteratoren gültig bleiben... solange nicht gerade das element selbst gelöscht wird. Insofern ist das hier schon gegeben. Ich denke nicht, dass gleichzeitiger lese- und schreibzugriff an unterschiedlichen Stellen ein Problem bei std::list sind.
Genaueres steht natürlich in der Doku der zugehörigen Implementierung. Die meisten sind afaik Thread-safe. Was allerdings nicht heißt, dass man beliebig parallel drin rumschreiben/lesen/löschen darf ohne dass jemals was passiert.
-
Undertaker schrieb:
sind sie leider nicht.
Und das ist auch richtig so. Vor allem: selbst wenn einzelne Operationen thread-sicher wären, böte das doch nur eine trügerische Sicherheit, die Race-Conditions geradezu hervorbeschwört.
Beispiel:
std::sort(container.begin(), container.end());Wäre nicht thread-sicher, selbst wenn container es wäre.
-
Naja, ein paar Sachen werde meist schon garantiert. Gleichzeitiges Lesen ist beispielsweise normalerweise schon okay. Viel mehr kann der Container natürlich selbst nicht bieten. Daher ist es das was ich unter Thread-safe für einen Container verstehen würde.
-
BorisDieKlinge schrieb:
Hallo,
ich benutze die std::list in zwei thread, wobei einer der thread nur elemente liest, der andere fügt elemente hinzu.. nun die frage ob std::list allgemein trheadsicher ist?
Threadsicherheit der Standardbibliothek ist implementationsabhängig; in wieweit sie bei dir gegeben ist, kann dir nur die Dokumentation deiner Plattform verraten.
In diesem speziellen Fall würde ich vermuten, dass eine 08/15-Implementierung für diese Art des Zugriffs nicht threadsicher ist: zwar bleiben Iteratoren und Referrnzen bei list-Operationen in der Regel gültig, dass bedeutet aber nicht, dass der Knoten, auf den diese Iteratoren verweisen, unveränderlich ist.
Immer dann, wenn Elemente hinzugefügt werden, müssen ja die Links der benachbarten Knoten entsprechend geändert werden. Falls der lesende Thread gerade mit diesem Knoten beschäftigt ist, und zum Beispiel gerade mit dem Iterator zum nächsten Element schreiten will, so liegt hier ganz offenbar ein Race Hazard vor, der sich (insbesondere bei einer doppelt verketteten Liste wie std::list) ohne Synchronisation nur schwer beseitigen lässt.
Für solche Fälle, wo Daten nur von einem in den anderen Thread transportiert werden müssen, bieten sich andere Strukturen - wie lock-freie Listen - besser an. Grundsätzlich garantieren die meisten Implementationen nur gleichzeitigen lesenden Zugriff. Alles andere bedarf dagegen des ausschließlichen Zugriffs durch nur einen Thread.
-
BorisDieKlinge schrieb:
...zwei thread, wobei einer der thread nur elemente liest, der andere fügt elemente hinzu...
Also ohne std::queue zu kennen: Für sowas nutzen wir Queues (MQSeries) - die sind genau für sowas gemacht. Die Eigenschaften einer "List" scheinst Du auch gar nicht zu benötigen.
(Damit will ich nicht sagen, dass Du Dich nicht mehr um threadsafety kümmern müsstest, aber sie ist vermutlich deutlich einfacher umzusetzen)Gruß,
Simon2.
-
Jester schrieb:
wettermann schrieb:
Dazu müssten ja alle Iteratoren und was man sich sonst wo von den containern holt, bei einer änderung upgedatet werden. Und so eine Art von "Threadsafe" wird es wohl bei keinem container geben.
std::list garantiert, dass iteratoren gültig bleiben... solange nicht gerade das element selbst gelöscht wird. Insofern ist das hier schon gegeben. Ich denke nicht, dass gleichzeitiger lese- und schreibzugriff an unterschiedlichen Stellen ein Problem bei std::list sind.
Genaueres steht natürlich in der Doku der zugehörigen Implementierung. Die meisten sind afaik Thread-safe. Was allerdings nicht heißt, dass man beliebig parallel drin rumschreiben/lesen/löschen darf ohne dass jemals was passiert.
Naja, kommt darauf an, wie unterschiedlich die unterschiedlichen Stellen sind. Wenn ich z.B hinter einem Element A ein neues einfüge und gleichzeitig, in einem anderen Thread, ein iter++ auf einen Iterator mache, der auf A zeigt, dann kann ich mir nicht vorstellen, dass das gut geht.
Mal was allgemeines. Wie ist der Begriff Threadsafe eigentlich definiert oder ist er überhaupt genau definiert? Ich kenne es eigentlich so, dass eine Funktion/Mehtode Threadsafe ist, wenn sie von mehreren Threads aufgerufen werden kann, ohne das in der Funktion selber ein Fehler auftritt. Also die Funktion verwendet intern keine globalen Variablen und sonst keine gleichen Resourcen (Dateien...). Die Daten mit denen die Funktion arbeitet sind aber nicht automatisch geschützt. Also wenn eine std::list threadsafe ist, dann heißt das, dass man die gleiche Funktion von zwei Threads aus aufrufen kann, wenn die zwei Threads mit unterschiedlichen std::list Instanzen arbeiten. Wenn die zwei Threads die gleiche Funktion der gleichen Instanz aufrufen, dann ist doch nicht garantiert, dass das sicher funktioniert. In Java gibt es da ja z.B. die synchronized Maps, Lists usw. die sperren immer die Collection, wenn eine Methode aufgerufen wird. Also threadsafe != synchronized. Aber synchronized heißt noch nicht, dass man die Liste an mehreren Stellen bearbeiten kann, ohne sie zu sperren, wenn man mehrere Elemente bearbeitet. Also iter.next() und list.add(xyz) aus verschiedenen Threads ist gleichzeitig nicht möglich. Man kann nur list.add(xyz) und list.get(0) "gleichzeitig" machen, weil die list gesperrt ist, wenn eine Methode ausgeführt wird.
Und man sollte allgemein auch noch bedenken, dass die Daten eines Objektes nicht automatisch geschützt sind, wenn ich die Liste die diese Elemente enthält durch Mutex oder so geschützt ist. Wenn man die Liste sperrt und sich ein Objekt holt und sie dann wieder frei gibt, kann es immer noch passieren, dass ein anderer Thread sich das selbe Objekt holt und dann beide in den Daten rumpfuschen.
-
wettermann schrieb:
Mal was allgemeines. Wie ist der Begriff Threadsafe eigentlich definiert oder ist er überhaupt genau definiert? Ich kenne es eigentlich so, dass eine Funktion/Mehtode Threadsafe ist, wenn sie von mehreren Threads aufgerufen werden kann, ohne das in der Funktion selber ein Fehler auftritt. Also die Funktion verwendet intern keine globalen Variablen und sonst keine gleichen Resourcen (Dateien...). Die Daten mit denen die Funktion arbeitet sind aber nicht automatisch geschützt.
Japp, so sehe ich das auch. Wie sonst wollte man es definieren? Komplexere Operationen kommen eigentlich nie ohne Synchronisation von außen aus.
-
Es gibt grundsätzlich 2 "Arten" von threadsafe:
-
Nur die "Funktionen" sind threadsafe, d.h. man kann verschiedene Objekte der selben Klasse in verschiedenen Threads verwenden (ohne synchronisieren zu müssen).
-
Die "Objekte" sind threadsafe, d.h. man kann ein und das selbe Objekt in verschiedenen Threads verwenden (ohne synchronisieren zu müssen).
----
(1) trifft auf fast alle STL bzw. C++ Standard-Library Implementierungen zu.
(2) auf keine mir bekannte.Jetzt mag einer denken (1) sei "ohnehin logisch", ist es aber nicht. Einige Funktionen der C++ Standard-Library brauchen die "current locale" -- was eine (verstecket) globale Variable darstellt. Der Zugriff auf diese muss synchronisiert werden, sonst kann man nichtmal (1) garantieren. Dummerweise ist das eine Sache die z.B. die MSVC Implementierung stellenweise ziemlich langsam macht (sowie wahrscheinlich auch jede andere Implementierung die (1) für alle Klassen - inklusive iostreams - garantiert). Weil eben an 100 Stellen die "current locale" geholt wird. Jeder stream-insert operator für integrale Typen macht das z.B.
Und (2) wird nicht implementiert eben weil es ganz furchtbar schlecht wäre, weil ganz furchtbar langsam (Laufzeit).
Ausnahmen sind hier natürlich Klassen die z.B. Synchronisierungs-Objekte kapseln wie z.B. Mutexen -- bloss solche Klassen kommen in der C++ Standard-Library (noch) nicht vor.
-