Frage

Wenn ich einen Wert in ein Feld schreiben, Welche Garantien bezüglich ich erhalte, wenn der neue Wert im Hauptspeicher gespeichert werden? Zum Beispiel: Wie kann ich wissen, dass der Prozessor nicht hält den neuen Wert in es private Cache ist, aber der Hauptspeicher aktualisiert?
Ein weiteres Beispiel:

int m_foo;

void Read() // executed by thread X (on processor #0)
{
   Console.Write(m_foo);
}

void Write() // executed by thread Y (on processor #1)
{
   m_foo = 1;
}

Gibt es eine Möglichkeit, dass nach Write () fertig ausgeführt wurde, einige andere Thread ausgeführt Read () aber tatsächlich "0" als den aktuellen Wert sehen? (Da vielleicht der vorherige Schreib zu m_foo wurde noch nicht geleert?).
Welche Art von Primitiven (neben Sperren) sind, wurden gespült zur Verfügung, um sicherzustellen, das die Schreibvorgänge?


Bearbeiten
Ich habe in dem Codebeispiel verwendet, die Schreib- und Lese bei verschiedenen Verfahren gestellt werden. Nicht Thread.MemoryBarrier nur Anweisung beeinflussen, die es gibt im gleichen Umfang Reording?

Auch nehmen sie an, dass sie nicht durch die JIT-inline sein, wie kann ich sicherstellen, dass der Wert auf m_foo geschrieben wird nicht in einem Register gespeichert werden, sondern in dem Hauptspeicher? (Oder wenn m_foo gelesen wird, wird es nicht einen alten Wert aus der CPU-Cache erhalten).

Ist es möglich, dies zu erreichen, ohne Schleusen oder über das ‚flüchtige‘ Stichwort? (Auch, sagen wir, ich bin nicht primitive Typen verwenden, sondern eine WORD Größe structs [so flüchtig nicht angewendet werden kann].)

War es hilfreich?

Lösung

Volatile und Verschlungene wurden bereits erwähnt, können Sie für Primitive gebeten, eine Aufnahme in die Liste ist Thread.MemoryBarrier(), bevor Sie schreibt zu verwenden oder liest. Dies garantiert keine Neuanordnung der Speicher schreibt getan wird und liest.

Dies tut „von Hand“, was lock, Interlocked und volatile automatisch die meiste Zeit tun können. Man könnte dies als vollwertiger Ersatz zu jeder anderen Technik verwenden, aber es ist wohl der härteste Weg zu reisen, und so sagt MSDN:

  

"Es ist schwierig, richtig multithreaded Programme zu erstellen, indem Sie   Memory. Für die meisten Zwecke der   C # lock-Anweisung, die Visual Basic   SyncLock Anweisung, und die Methoden der   die Monitor-Klasse bietet einfacher und   weniger fehleranfällig Wege zu synchronisieren   Zu. Wir empfehlen Ihnen,   verwenden sie anstelle von Memory. „

Wie verwenden Memory

Ein sehr schönes Beispiel sind die Implementierungen von VolatileRead und VolatileWrite, dass sowohl intern MemoryBarrier verwenden. Die Faustregel zu folgen ist: wenn Sie eine Variable zu lesen, eine Speicherbarriere platzieren nach der Lese. Wenn Sie den Wert schreiben, muss die Speicherbarriere kommen vor die Schreib.

Falls Sie Zweifel haben, ob dies weniger effizient dann lock ist, der Ansicht, dass Verriegelungs nichts mehr ist dann „voll Fechten“, dass sie eine Speicherbarriere stellt vor und nach dem Codeblock (Monitor für einen Moment zu ignorieren). Dieses Prinzip ist auch in diesem erläuterte ausgezeichneten endgültige Artikel über Gewinde, Sperren, flüchtige Speicher und Barrieren Albahari .

Von Reflektor:

public static void VolatileWrite(ref byte address, byte value)
{
    MemoryBarrier();
    address = value;
}

public static byte VolatileRead(ref byte address)
{
    byte num = address;
    MemoryBarrier();
    return num;
}

Andere Tipps

Wenn Sie sicherstellen wollen, dass es schnell geschrieben und in Ordnung, markieren Sie es dann als volatile , oder (mit mehr Schmerz) Gebrauch Thread.VolatileRead / Thread.VolatileWrite (keine attraktive Option, und einfach zu vermissen eine, es nutzlos).

volatile int m_foo;

Ansonsten haben Sie praktisch keine Garantie für irgendetwas (sobald Sie mehrere Threads sprechen).

Sie können auch Sperren aussehen wollen ( Monitor ) oder Interlocked , die das gleiche erreichen würde Effekt solange der gleiche Ansatz von alle Zugriff verwendet wird (dh alle lock oder alle Interlocked, etc).

Solange Sie verwenden keine Synchronisation haben Sie keine Garantie, dass ein Thread auf einem Prozessor laufen sieht die von einem anderen Thread Änderungen auf einem anderen Prozessor ausgeführt wird. Das ist, weil der Wert in dem CPU-Cache-Speicher zwischengespeichert werden könnte oder in einem CPU-Register.

Daher müssen Sie entweder die Variable als flüchtig markieren. Das wird eine ‚Happens-Before'-realation schafft zwischen einem Schreiben liest.

Das ist kein Prozessor-Cache-Problem. Writes sind in der Regel Pass-Through (schreibt gehen beide Cache und Hauptspeicher) und alle liest, wird Zugriff auf Cache. Aber es gibt viele andere Caches auf dem Weg (Programmiersprache, Bibliotheken, Betriebssystem, I / O Puffer, etc.). Der Compiler kann auch eine Variable in einem Prozessorregister zu halten zu wählen und es nie in dem Hauptspeicher zu schreiben (das ist, was der flüchtige Betreiber ausgelegt ist, vermeidet Wert im Register zu speichern, wenn es dich um einen Speicher-I / O zugeordnet werden kann).

Wenn Sie mehrere Prozesse oder mehrere Threads und Synchronisierung haben, ist ein Problem, das Sie es explictly tun müssen, gibt es viele, wie es auf den Anwendungsfall abhängig zu machen.

Für ein Single-Threaded-Programm, egal, wird der Compiler tun, was er muss, und liest den Zugang zu dem, was geschrieben wurde.

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