Frage

Lassen Sie uns sagen, dass eine Klasse ein public int counter Feld hat, das von mehreren Threads zugegriffen wird. Diese int wird nur erhöht oder erniedrigt.

Dieses Feld ist auf erhöhen, was Ansatz verwendet werden soll, und warum?

  • lock(this.locker) this.counter++;,
  • Interlocked.Increment(ref this.counter);,
  • Ändern Sie den Zugriffsmodifizierer von counter public volatile.

Nun, da ich volatile entdeckt habe, habe ich viele lock Aussagen und die Verwendung von Interlocked worden zu entfernen. Aber gibt es einen Grund, dies nicht zu tun?

War es hilfreich?

Lösung

Schlimmste (wird nicht wirklich funktionieren)

  

Ändern Sie den Zugriffsmodifizierer von counter public volatile

Wie andere Leute schon erwähnt haben, dies allein ist gar nicht wirklich sicher. Der Punkt der volatile ist, dass mehrere Threads auf mehreren CPUs laufen können und Daten zwischengespeichert werden und erneut um Anweisungen.

Wenn es nicht volatile und CPU-A erhöht einen Wert, dann kann die CPU-B nicht wirklich sehen, dass inkrementierten Wert erst einige Zeit später, was zu Problemen führen kann.

Wenn es volatile ist, dies stellt sicher, nur die beiden CPUs die gleichen Daten zur gleichen Zeit zu sehen. Es macht mich nicht stoppen überhaupt von Verschachtelung ihrer Lese- und Schreiboperationen, die das Problem, das Sie versuchen zu vermeiden.

Second Best:

  

lock(this.locker) this.counter++;

Dies ist sicher zu machen (vorausgesetzt, Sie überall sonst lock daran erinnern, dass Sie this.counter zugreifen). Es wird verhindert, dass andere Threads von anderem Code ausführt, der durch locker bewacht wird. Mit Sperren auch verhindert, dass die Multi-CPU-Neuordnungs Probleme wie oben, was toll ist.

Das Problem ist, Verriegelung ist langsam, und wenn Sie die locker an einem anderen Ort wieder verwendet werden, die nicht wirklich verwandt ist, dann können Sie Ihre andere Threads ohne Grund am Ende blockiert wird.

Best

  

Interlocked.Increment(ref this.counter);

Dies ist sicher, da es effektiv die Lese-, erhöht den Fall ist, und schreibt in ‚einen Hit‘, die nicht unterbrochen werden kann. Aus diesem Grund, wird es keinen anderen Code beeinflussen, und Sie brauchen sich nicht zu erinnern, an anderer Stelle entweder zu sperren. Es ist auch sehr schnell (wie MSDN sagt, auf modernen CPUs, ist dies oft buchstäblich ein einzelner CPU-Befehl).

Ich bin nicht ganz sicher, ob aber es wird um andere CPUs Dinge Neuordnen, oder wenn Sie auch mit dem Zuwachs flüchtigen kombinieren müssen.

InterlockedNotes:

  1. VERRIEGELT METHODEN gleichzeitig SAFE auf eine Anzahl von Cores oder CPUs.
  2. Verriegelten Methoden anwenden einen vollständigen Zaun um Anweisungen, die sie ausführen, so Umordnung nicht geschieht.
  3. Verriegelten Methoden nicht braucht oder sogar unterstützt keinen Zugriff auf ein flüchtiges Feld , wie flüchtig einen halben Zaun um Operationen auf bestimmten Bereich platziert und verriegelt wird, um den vollen Zaun verwendet wird.

Fußnote: Was für wirklich gute flüchtig ist

.

Wie volatile nicht diese Art von Multi-Threading Probleme verhindern, was ist es? Ein gutes Beispiel sagt man zwei Threads hat, eine, die immer auf eine Variable schreibt (sagen queueLength), und eine, die immer von derselben Variable liest.

Wenn queueLength nicht flüchtig ist, Thread A fünfmal schreiben kann, aber Thread B kann diese schreibt als verzögert (oder sogar möglicherweise in der falschen Reihenfolge) sehen.

wäre eine Lösung zu sperren, aber man könnte auch in dieser Situation volatil verwenden. Dies würde das Gewinde B sicherzustellen, wird immer die meisten up-to-date, was sehen, dass Thread A geschrieben hat. Beachten Sie jedoch, dass diese Logik nur funktioniert, wenn Sie Autoren haben, die nie lesen und Leser, die nie schreiben, und , wenn das Ding Sie schreiben ein atomarer Wert ist. Sobald Sie eine einzelne Read-Modify-Write tun, müssen Sie Verschlungene Operationen gehen oder eine Sperre verwendet werden.

Andere Tipps

EDIT: Wie in den Kommentaren erwähnt, in diesen Tagen bin ich glücklich Interlocked für die Fälle a einzelne Variable zu verwenden, wo es ist offensichtlich in Ordnung. Wenn es komplizierter wird, werde ich noch zurückkommen zu Sperren ...

Mit volatile wird nicht helfen, wenn Sie zu erhöhen, müssen - da die Lese- und der Schreib sind separate Anleitung. Ein anderer Thread könnte den Wert ändern, nachdem Sie gelesen haben, aber bevor Sie schreiben zurück.

Persönlich ich fast immer nur sperren - es einfacher ist, direkt in einer Art und Weise zu erhalten, die ist offensichtlich Recht als entweder Volatilität oder Interlocked.Increment. Soweit es mich betrifft, Lock-Free Multi-Threading ist für echte Threading-Experten, von denen ich bin nicht ein. Wenn Joe Duffy und sein Team schöner Bibliotheken bauen, die Dinge ohne so viel Verriegelung als etwas parallelisieren will ich bauen würde, das ist fabelhaft, und ich werde es in einem Herzschlag verwenden - aber wenn ich das Einfädeln selbst mache, versuche ich zu halten sie es einfach.

"volatile" ersetzt nicht Interlocked.Increment! Es macht einfach sicher, dass der Variable nicht im Cache gespeichert, sondern direkt verwendet.

eine Variable Inkrementieren erfordert tatsächlich drei Operationen:

  1. lesen
  2. Schrittweite
  3. Schreib

Interlocked.Increment führt alle drei Teile als eine einzige atomare Operation.

Lock oder verriegelt Schritt ist das, was Sie suchen.

Flüchtige ist definitiv nicht, was Sie nach -. Es einfach, den Compiler erzählt die Variable wie immer zu ändern, auch wenn der aktuelle Codepfad zu behandeln, kann der Compiler sonst eine Lese aus dem Speicher optimieren

z.

while (m_Var)
{ }

Wenn m_Var in einem anderen Thread auf false gesetzt ist, aber es ist nicht so flüchtig erklärt, ist der Compiler frei, es eine Endlosschleife zu machen (was aber nicht bedeutet, es wird immer), indem sie es überprüft gegen ein CPU-Register (zB EAX denn das war es, was m_Var in von Anfang an) geholt wurde stattdessen ein anderes Lese auf den Speicherplatz von m_Var der Ausgabe (diese im Cache gespeichert werden kann - wir wissen nicht, und kümmern sich nicht, und das ist der Punkt, der Cache-Kohärenz von x86 / x64). Alle Beiträge früher von anderen, die Anweisung Neuordnungs erwähnt einfach zeigen, dass sie nicht x86 / x64-Architekturen verstehen. Volatile tut nicht Ausgabe Lese- / Schreibsperren, wie durch die früheren Beiträge implizierten sagen: ‚es verhindert Neuordnungs‘. In der Tat, dank wieder MESI-Protokoll, werden wir das Ergebnis, das wir immer das gleiche lesen garantiert über CPUs ist unabhängig davon, ob die tatsächlichen Ergebnisse zu physischen Speichern oder einfach wohnen in der lokalen CPU-Cache im Ruhestand. Ich will nicht zu weit in die Details dieses gehen, aber seien Sie versichert, dass, wenn das schief geht, Intel / AMD wahrscheinlich einen Prozessor Rückruf ausgeben würde! Das bedeutet auch, dass wir uns aus der Auftragsausführung nicht kümmern usw. Die Ergebnisse sind immer um in den Ruhestand garantiert - andernfalls wir sind gefüllt

Mit Verschlungene Erhöhungsschritten, muss der Prozessor gehen, um, holen Ihnen den Wert aus der angegebenen Adresse, dann erhöhen und schreibt sie zurück - und das alles, während ausschließlichen Besitz der gesamten Cache-Zeile mit (lock XADD) um sicherzustellen, dass keine andere Prozessoren können den Wert ändern.

Mit flüchtigen, werden Sie immer noch mit nur 1 Befehl am Ende (die JIT Annahme ist effizient, wie es sollte) - inc dword ptr [m_Var]. Allerdings ist der Prozessor (CPUA) nicht für exklusiven Besitz der Cache-Zeile zu stellen, während alle tut es mit der Sperrungs Version tat. Wie Sie sich vorstellen können, bedeutet dies, andere Prozessoren einen aktualisierten Wert zurück zu m_Var schreiben können, nachdem es von CPUA gelesen worden ist. Anstatt also jetzt den Wert zweimal erhöht haben, erhalten Sie nur einmal auf.

Hope dies löscht das Problem auf.

Weitere Informationen finden Sie unter 'Verstehen Sie die Auswirkungen von Low-Lock Techniques in Multithreaded Apps' - http://msdn.microsoft.com/en-au/magazine/cc163715.aspx

P. S. Was hat diese sehr späte Antwort? Alle Antworten waren so eklatant falsch (besonders die als Antwort markiert) in ihrer Erklärung, die ich musste es einfach zu klären, für jemand anderes dieses zu lesen. zuckt

p.p.s. Ich gehe davon aus, dass das Ziel x86 / x64 und nicht IA64 (es hat ein anderes Speichermodell). Beachten Sie, dass Microsoft ECMA-Spezifikationen in verkorkst sind, dass es das schwächste Speichermodell anstelle der stärksten gibt (es ist immer besser gegen das stärkste Speichermodell angeben, damit es über Plattformen hinweg konsistent ist - ansonsten Code, 24-7 auf x86 laufen würde / x64 kann gar nicht auf IA64 laufen, obwohl Intel ähnlich starkes Speichermodell für IA64) implementiert haben - Microsoft diese selbst zugegeben - http://blogs.msdn.com/b/cbrumme/archive/2003/05/17/51445.aspx .

Verriegelte Funktionen nicht blockieren. Sie sind atomar, was bedeutet, dass sie ohne die Möglichkeit eines Kontextwechsel während Schritt abschließen können. Es gibt also keine Chance Deadlock ist oder warten.

Ich würde sagen, dass Sie es immer zu einem Schloss und Schritt bevorzugen sollen.

Flüchtige ist nützlich, wenn Sie in einem Thread müssen, schreibt in einem anderen zu lesen, und wenn Sie wollen, dass der Optimierer keine Operationen auf eine Variable neu anordnen (weil die Dinge in einem anderen Thread passiert, dass der Optimierer nicht kennt). Es ist eine orthogonale Wahl, wie Sie erhöhen.

Dies ist ein wirklich guter Artikel, wenn Sie mehr über Lock-freien Code lesen wollen, und der richtige Weg, es zu nähern Schreiben

http://www.ddj.com/hpc-high-performance- Rechen- / 210604448

Schloss (...) funktioniert, kann aber einen Thread blockiert und konnte Deadlock führen, wenn ein anderer Code ist die gleichen Schlösser in einer inkompatiblen Weise verwendet wird.

verzahnt. * Ist der richtige Weg, es zu tun ... viel weniger Overhead als moderner CPUs unterstützt dies als primitiv.

flüchtig auf seinem eigenen ist nicht korrekt. Ein Thread zum Abrufen und dann zurückschreiben einen modifizierten Wert versuchen könnte noch mit einem anderen Thread, die die gleichen Konflikt geraten.

ich einigen Test tat, um zu sehen, wie eigentlich die Theorie funktioniert: kennethxu.blogspot.com/2009/05/interlocked-vs-monitor-performance.html . Mein Test war mehr auf CompareExchnage aber das Ergebnis für Increment ist ähnlich. Verschlungene ist nicht notwendig, schneller in Multi-CPU-Umgebung. Hier ist das Testergebnis für Erhöhungsschritte auf einem 2 Jahre alten 16-CPU-Server. Bare daran, dass der Test auch die sicheren Lesen nach dem Anstieg beinhaltet, die in der realen Welt typisch ist.

D:\>InterlockVsMonitor.exe 16
Using 16 threads:
          InterlockAtomic.RunIncrement         (ns):   8355 Average,   8302 Minimal,   8409 Maxmial
    MonitorVolatileAtomic.RunIncrement         (ns):   7077 Average,   6843 Minimal,   7243 Maxmial

D:\>InterlockVsMonitor.exe 4
Using 4 threads:
          InterlockAtomic.RunIncrement         (ns):   4319 Average,   4319 Minimal,   4321 Maxmial
    MonitorVolatileAtomic.RunIncrement         (ns):    933 Average,    802 Minimal,   1018 Maxmial

Ich mag erwähnt in den anderen Antworten der Differenz zwischen volatile, Interlocked und lock hinzuzufügen:

Das flüchtige Schlüsselwort kann auf Felder aufgebracht werden diese Art :

  • Referenztypen.
  • Zeigertypen (in einem unsicheren Kontext). Beachten Sie, dass, obwohl der Zeiger selbst flüchtig sein kann, das Objekt, dass es auf die Punkte nicht. In anderen Worte, können Sie nicht ein „Zeiger“ zu sein, „flüchtig“.
  • erklären
  • Einfache Typen wie sbyte, byte, short, ushort, int, uint, char, float und bool.
  • Ein Aufzählungstyp mit einem der folgenden Grundtypen:. byte, sbyte, short, ushort, int oder uint
  • Allg Typparameter bekannt Referenztypen sein.
  • IntPtr und UIntPtr.

Andere Typen , einschließlich double und long können nicht markiert werden "flüchtig" da liest und schreibt kann auf Felder dieser Typen nicht garantiert werden sein Atom. Zum Schutz der Multi-Threaded-Zugangs zu diesen Arten von Felder, die Interlocked Klassenmitglieder verwenden oder Zugriff über den Schutz lock Aussage.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top