Wenn ich auf einen einzelnen Integer-Typ (z. B. long, int, bool usw.) in mehreren Threads zugreife, muss ich einen Synchronisationsmechanismus wie einen Mutex verwenden, um sie zu sperren. Mein Verständnis ist, dass ich als atomare Typen den Zugriff auf einen einzelnen Thread nicht sperren muss, aber ich sehe eine Menge Code, der Locking verwendet. Das Profiling eines solchen Codes zeigt, dass die Verwendung von Sperren einen erheblichen Leistungseinbruch bedeutet. Also, wenn der Gegenstand, auf den ich zugreife, einer ganzen Busbreite entspricht (z. B. 4 Bytes auf einem 32-Bit-Prozessor), muss ich den Zugriff darauf sperren, wenn er über mehrere Threads hinweg verwendet wird? Anders ausgedrückt: Wenn Thread A zur gleichen Zeit wie der Thread B von der gleichen Variablen in die Integer-Variable X schreibt, ist es möglich, dass Thread B ein paar Bytes des vorherigen Wertes mit ein paar Bytes davon gemischt hat Der Wert wird geschrieben? Ist diese Architektur abhängig, z. ok für 4-Byte-Ganzzahlen auf 32-Bit-Systemen, aber unsicher auf 8-Byte-Ganzzahlen auf 64-Bit-Systemen?
Bearbeiten: Habe gerade diesen zugehörigen Post gesehen hilft ein gutes Stück.
Sie sperren niemals einen Wert - Sie sperren einen Vorgang auf einen Wert.
C & amp; In C ++ werden Threads oder atomare Operationen nicht explizit erwähnt - Operationen, die aussehen, als könnten sie atomar sein oder nicht, werden von der Sprachspezifikation nicht als atomar bestätigt.
Es wäre zugegebenermaßen ein ziemlich abweichender Compiler, der einen nicht-atomaren Read auf einem int verwaltet: Wenn Sie eine Operation haben, die einen Wert liest, ist es wahrscheinlich nicht nötig, sie zu schützen. Es kann jedoch nicht atomar sein, wenn es eine Maschinenwortgrenze überspannt.
Operationen wie m_counter++
beinhalten eine Abruf-, Inkrementierungs- und Speicheroperation - eine Race-Bedingung: ein anderer Thread kann den Wert nach dem Fetch, aber vor dem Speicher ändern - und muss daher durch einen Mutex - ODER-Fund geschützt werden Ihre Compiler unterstützen Interlocked-Operationen. MSVC hat Funktionen wie _InterlockedIncrement (), die einen Speicherplatz sicher inkrementieren, solange alle anderen Schreibvorgänge in ähnlicher Weise eine verriegelte Apis verwenden, um den Speicherort zu aktualisieren - was um Größenordnungen leichter ist als das Aufrufen eines sogar kritischen Abschnitts.
GCC hat intrinsische Funktionen wie __sync_add_and_fetch
, die auch dazu verwendet werden können, verblockte Operationen an Maschinenwortwerten auszuführen.
In C ++ gibt es keine Unterstützung für atomare Variablen, daher müssen Sie sperren. Ohne Sperren können Sie nur darüber spekulieren, welche genauen Anweisungen für die Datenmanipulation verwendet werden und ob diese Anweisungen den atomaren Zugriff garantieren - so entwickeln Sie keine zuverlässige Software.
Ja, es wäre besser, die Synchronisation zu verwenden. Alle Daten, auf die mehrere Threads zugreifen, müssen synchronisiert werden.
Wenn es Windows-Plattform ist, können Sie auch hier überprüfen: Interlocked Variable Access .
Wenn Sie sich auf einem Rechner mit mehr als einem Kern befinden, brauchen Sie , um die Dinge richtig auszuführen, obwohl die Schreibvorgänge einer Ganzzahl atomar sind. Die Probleme sind zweifach:
Wenn es nur die erste Sache wäre, wäre es okay, die Variable volatile
zu markieren, aber die zweite ist wirklich der Mörder und Sie werden nur den Unterschied auf einem Multicore-Rechner sehen . Das ist eine Architektur, die viel häufiger wird als früher ... Ups! Zeit aufzuhören, schlampig zu sein; Verwenden Sie den richtigen Mutex- (oder Synchronisierungs- oder was auch immer) Code für Ihre Plattform und alle Details, wie Speicher funktioniert, so wie Sie glauben, dass er verschwindet.
In 99,99% der Fälle müssen Sie sperren , auch wenn es sich um scheinbar atomare Variablen handelt. Da der C ++ - Compiler das Multi-Threading auf Sprachenebene nicht kennt, kann er viele nicht-triviale Umordnungen durchführen.
Fall-in-Punkt: Ich wurde von einer Spin-Lock-Implementierung gebissen, bei der unlock einfach einer volatile
-Integer-Variablen eine Null zuweist. Der Compiler ordnete die Entsperrungsoperation vor der eigentlichen Operation unter der Sperre neu an, was zu mysteriösen Abstürzen führte.
Siehe:
Multithreading ist schwierig und komplex. Die Anzahl der schwer zu diagnostizierenden Probleme, die auftreten können, ist ziemlich groß. Insbesondere auf Intel-Architekturen ist das Lesen und Schreiben von ausgerichteten 32-Bit-Ganzzahlen garantiert atomar im Prozessor, aber das bedeutet nicht, dass es in Multithread-Umgebungen sicher ist.
Ohne entsprechende Wächter können der Compiler und / oder der Prozessor die Anweisungen in Ihrem Codeblock neu anordnen. Es kann Variablen in Registern zwischenspeichern und sie sind nicht in anderen Threads sichtbar ...
Das Sperren ist teuer, und es gibt verschiedene Implementierungen von lock-less-Datenstrukturen, die für eine hohe Leistung optimiert werden sollen, aber es ist schwierig, dies richtig zu tun. Und das Problem ist, dass Nebenläufigkeitsfehler normalerweise unklar und schwer zu debuggen sind.
Ja. Wenn Sie unter Windows arbeiten, können Sie sich Interlocked Funktionen / Variablen ansehen und wenn Sie von der Boost-Überzeugung sind, dann können Sie sich ihre Implementierung atomarer Variablen anschauen .
Wenn der Boost zu schwer ist und " atomic c ++ " in Ihre Lieblings-Suchmaschine wird Ihnen viel zu denken geben.
Tags und Links c++ multithreading atomic