Laden...

volatile und Multithreading - Race bei i += 7

Erstellt von gunnag vor 8 Jahren Letzter Beitrag vor 8 Jahren 1.250 Views
G
gunnag Themenstarter:in
3 Beiträge seit 2015
vor 8 Jahren
volatile und Multithreading - Race bei i += 7

Hallo zusammen,

es geht um C#, .NET und multithreading. Soweit ihc weiß sind lese und schreibzugriffe auf datentypen ≤ native int atomar und das schlüsselwort volatile gibt mir eine acquire und release semantic (also keinen full-fence).

Ich habe folgenden Ausschnitt und frage mich warum meine race-detektoren hier einen data race anzeigen im "get { ...}" und auf der code-zeile in thread 2? Ist das ein false positive?


private volatile int m_my_int;

//auf thread 1 ausgeführt
public int get_int
{
    get { return this.m_my_int; }
}


// auf thread 2 ausgeführt
this.m_my_int += 7;

S
248 Beiträge seit 2008
vor 8 Jahren

Hallo gunnag,

das Schlüsselwort volatile gibt mir eine acquire und release semantic (also keinen full-fence).

Das volatile Schlüsselwort bewirkt, dass der Inhalt nicht gecacht, sondern bei jedem (lesenden) Zugriff neu aus dem Speicher in ein Register kopiert wird.

Eine genauere Beschreibung findest du hier:
volatile (C#-Referenz)

Ich frage mich eher, ob die Variable als volatile deklariert werden muss. Dies ist in diesem Fall vermutlich unsinnig, da der Property-getter die Variable eh nicht cachen kann und frisch aus dem Speicher auslesen muss. Solange nur Thread 2 die Variable ändert, kannst du volatile weglassen.

Grüße
spooky

1.029 Beiträge seit 2010
vor 8 Jahren

Hi,

die Frage kann ich zwar nicht sicher beantworten - aber mindestens das volatile muss man verwenden, da sonst der Wert ja auch durchaus in einem Register stecken kann. (Unwahrscheinlich - ja - aber möglich soweit ich weiß)

Folgende Erklärung fand ich super:
Volatility, Atomicity and Interlocking

LG

6.911 Beiträge seit 2009
vor 8 Jahren

Hallo gunnag,

das Race kommt vom += von this.m_my_int += 7;

Das ist nicht atomar, denn was passiert hier:1. der Wert von m_my_int wird direkt aus dem RAM in ein (CPU-) Register gelesen (wegen volatile)

  1. der Wert im Register, dort wo jetzt also der Wert von m_my_int ist, wird um 7 erhöht
  2. der neue Wert im Register wird (wegen des C# MemoryModels*, hier nicht wegen volatile) in den RAM geschrieben

Wärhend dieser 3 Schritte kann also ein Race auftreten. Daher sollte hier eine Interlocked-Methode verwendet werden.

* C# hat ein "strong memory model" und somit ist jeder Schreibvorgang volatile by default. Das Verhalten von C# beim Schreiben wird auch als "strong volatile semantics" bezeichnet.

meine race-detektoren

Welche verwendest du da?

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

W
872 Beiträge seit 2005
vor 8 Jahren

Volatile Almost useless for multithread programming und atomicity, volatility and immutability are different
geben Dir mehr Anregungen.
Meine Erfahrungen mit Interlocked sind nicht so positiv - im Vergleich zu lock war es eher langsam...

G
gunnag Themenstarter:in
3 Beiträge seit 2015
vor 8 Jahren

Danke Euch! Hilft schonmal weiter.

@gfoidl: Intel Inspector momentan

4.931 Beiträge seit 2008
vor 8 Jahren

Generell zum Thema Multi-Threading kann ich Threading in C# (by Joseph Albahari) empfehlen.
Speziell: The volatile keyword

Ich würde, so wie gfoidl, hier auch die Interlocked-Operationen empfehlen.

6.911 Beiträge seit 2009
vor 8 Jahren

Hallo weismat,

im Vergleich zu lock war es eher langsam

Das kann schon sein, denn (speziell ab .net 4.0) wurde das optimiert, so dass eine Zeit lang "gespinnt" wird bevor zu einem richtigen lock per Monitor.Enter/Exit übergegangen wird.

Pauschal kann aber keine Aussage getroffen werden ob Interlocked od. lock schneller ist, denn es kommt immer auf die tatsächlich vorhandene Hardware und den ganeuen Anwendungsfall an.
Durch einen Profiler kann das aber getestet werden. Sonst sollte Code in erster Linie leserlich und nachvollziehbar geschrieben werden und "premature optimization is the root of all evil" (Donald E. Knuth) berücksichtigt werden. Gut bei Interlocked vs. lock wird der Code so od. nicht unleserlicher 😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

G
gunnag Themenstarter:in
3 Beiträge seit 2015
vor 8 Jahren

Hallo,

ich bin über folgendes gestolpert und würde gerne Eure Meinung dazu einholen:

hier steht: The C# Memory Model in Theory and Practice

Ein Nachteil besteht darin, dass auch Typen, die normalerweise atomar gelesen und geschrieben werden (wie „int“), nicht atomar gelesen oder geschrieben werden können, wenn der Wert im Speicher nicht korrekt ausgerichtet ist. In C# ist die korrekte Ausrichtung von Werten normalerweise garantiert. Der Benutzer kann die Ausrichtung jedoch mithilfe der StructLayoutAttribute-Klasse überschreiben (bit.ly/Tqa0MZ).

ist das Korrekt? Das würde ja bedeuten, dass die primitive datatypes doch nicht atomar lesbar/schreibbar sind?

2.298 Beiträge seit 2010
vor 8 Jahren

ist das Korrekt? Das würde ja bedeuten, dass die primitive datatypes doch nicht atomar lesbar/schreibbar sind?

In der Regel wirst du für einen int keine Änderung an der Ausrichtung vornehmen. Allgemein habe ich sowas bisher nur selten gesehen (z.B. bei Komponenten zum Zugriff auf OPC-Server).

Klar ändert sich was am Verhalten von X, wenn du X änderst, solang du das aber nicht tust sollte der Zugriff weiterhin atomar sein.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

S
248 Beiträge seit 2008
vor 8 Jahren

In deinem Verlinkten Artikel steht ebenfalls:

Die C# ECMA-Spezifikation garantiert, dass die folgenden Typen atomar geschrieben werden: Verweistypen, „bool“, „char“, „byte“, „sbyte“, „short“, „ushort“, „uint“, „int“ und „float“.

Ich vermute dass 64bit Datentypen in einem 64bit Prozess ebenfalls atomar geschrieben werden können.

6.911 Beiträge seit 2009
vor 8 Jahren

Hallo Spook,

stimmt. Siehe dazu auch

Zitat von: Interlocked.Read Method (Int64)(System.Threading)
The Read method is unnecessary on 64-bit systems, because 64-bit read operations are already atomic. On 32-bit systems, 64-bit read operations are not atomic unless performed using Read.

The Read method and the 64-bit overloads of the Increment, Decrement, and Add methods are truly atomic only on systems where a System.IntPtr is 64 bits long.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"