Das Schlimmste (funktioniert eigentlich nicht)
Ändern Sie den Zugriffsmodifikator von counter
inpublic volatile
Wie andere bereits erwähnt haben, ist dies allein überhaupt nicht sicher. Der Punkt volatile
ist, dass mehrere Threads, die auf mehreren CPUs ausgeführt werden, Daten zwischenspeichern können und werden und Anweisungen neu anordnen.
Wenn dies nicht der volatile
Fall ist und CPU A einen Wert erhöht, sieht CPU B diesen inkrementierten Wert möglicherweise erst einige Zeit später, was zu Problemen führen kann.
Wenn volatile
dies der Fall ist , wird nur sichergestellt, dass die beiden CPUs gleichzeitig dieselben Daten sehen. Es hindert sie überhaupt nicht daran, ihre Lese- und Schreibvorgänge zu verschachteln, was das Problem ist, das Sie vermeiden möchten.
Zweitbester:
lock(this.locker) this.counter++
;;
Dies ist sicher (vorausgesetzt, Sie erinnern sich an alle lock
anderen Stellen, auf die Sie zugreifen this.counter
). Es verhindert, dass andere Threads anderen Code ausführen, der von geschützt wird locker
. Die Verwendung von Sperren verhindert auch die oben beschriebenen Probleme bei der Neuordnung mehrerer CPUs, was großartig ist.
Das Problem ist, dass das Sperren langsam ist. Wenn Sie das locker
an einem anderen Ort wiederverwenden, der nicht wirklich verwandt ist, können Sie Ihre anderen Threads ohne Grund blockieren.
Beste
Interlocked.Increment(ref this.counter);
Dies ist sicher, da das Lesen, Inkrementieren und Schreiben effektiv in einem Treffer ausgeführt wird, der nicht unterbrochen werden kann. Aus diesem Grund wirkt sich dies nicht auf anderen Code aus, und Sie müssen auch nicht daran denken, an anderer Stelle zu sperren. Es ist auch sehr schnell (wie MSDN sagt, ist dies auf modernen CPUs oft buchstäblich eine einzelne CPU-Anweisung).
Ich bin mir jedoch nicht ganz sicher, ob es darum geht, dass andere CPUs Dinge neu anordnen oder ob Sie auch flüchtig mit dem Inkrement kombinieren müssen.
InterlockedNotes:
- VERRIEGELTE METHODEN SIND AUF JEDER ANZAHL VON KERNEN ODER CPUS KONZURENT SICHER.
- Interlocked-Methoden wenden einen vollständigen Zaun um die von ihnen ausgeführten Anweisungen an, sodass keine Neuordnung erfolgt.
- Interlocked-Methoden benötigen oder unterstützen den Zugriff auf ein flüchtiges Feld nicht , da Volatile einen halben Zaun um Operationen auf einem bestimmten Feld legt und Interlocked den vollständigen Zaun verwendet.
Fußnote: Wofür flüchtig ist eigentlich gut.
Wie volatile
nicht diese Art von Multithreading - Probleme zu verhindern, was ist es? Ein gutes Beispiel ist, dass Sie zwei Threads haben, einen, der immer in eine Variable schreibt (z. B. queueLength
), und einen, der immer aus derselben Variablen liest.
Wenn queueLength
es nicht flüchtig ist, kann Thread A fünfmal schreiben, aber Thread B kann diese Schreibvorgänge als verzögert (oder möglicherweise sogar in der falschen Reihenfolge) ansehen.
Eine Lösung wäre das Sperren, aber Sie könnten in dieser Situation auch volatile verwenden. Dies würde sicherstellen, dass Thread B immer das aktuellste sieht, was 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, was Sie schreiben, ein atomarer Wert ist. Sobald Sie ein einzelnes Lese-, Änderungs- und Schreibvorgang ausführen, müssen Sie zu Interlocked-Vorgängen wechseln oder eine Sperre verwenden.