2010-08-29 5 views
6

Ich habe eine Frage zum C# -Memory-Modell und den Threads. Ich bin mir nicht sicher, ob der folgende Code ohne das flüchtige Schlüsselwort korrekt ist.C# -Memory-Modell und nichtflüchtige Variable, die vor der Erstellung der anderen Threads initialisiert wurde

public class A { 
    private int variableA = 0; 

    public A() { 

    variableA = 1; 

    Thread B = new Thread(new ThreadStart(() => printA())).Start(); 
    } 

    private void printA() { 
    System.Console.WriteLine(variableA); 
    } 
} 

Meine Sorge ist, wenn sichergestellt ist, dass das Thema B VariableA mit dem Wert 1 ohne Verwendung flüchtige sehen? Im Hauptthread weise ich der VariableA im Konstruktor nur 1 zu. Danach werde ich VariableA nicht berühren, es wird nur in Thread B verwendet, also ist das Sperren wahrscheinlich nicht notwendig.

Aber ist es garantiert, dass der Haupt-Thread seinen Cache leeren und den Inhalt von VariableA in den Hauptspeicher schreiben wird, damit der zweite Thread den neu zugewiesenen Wert lesen kann?

Ist es außerdem garantiert, dass der zweite Thread den Inhalt von VariableA aus dem Hauptspeicher liest? Können einige Compileroptimierungen auftreten und der Thread B kann den Inhalt der Variable A aus dem Cache anstelle des Hauptspeichers lesen? Es kann passieren, wenn die Reihenfolge der Anweisungen geändert wird.

Sicherlich wird das Hinzufügen des flüchtigen der VariableA-Deklaration den Code korrekt machen. Aber, ist es notwendig? Ich frage, weil ich einige Code mit einigen nicht flüchtigen Variablen Initialisierung im Konstruktor geschrieben hat, und die Variablen später von einigen Timer-Threads verwendet werden, und ich bin mir nicht sicher, ob es völlig korrekt ist.

Was ist mit dem gleichen Code in Java?

Danke, Michal

Antwort

4

Der gleiche Code in Java ist auf jeden Fall in Ordnung - die Schaffung eines neuen Thread dient als eine Art Barriere, effektiv. (Alle Aktionen früher im Programmtext als die Thread-Erzeugung „geschehen, bevor“ der neuen Thread startet.)

Ich weiß nicht, was in Bezug auf neue Thread-Erzeugung in .NET garantiert wird, aber. Noch beunruhigender ist die Möglichkeit eines verzögerten Lesens bei Verwendung von Control.BeginInvoke und dergleichen ... Ich habe keine Garantien für Speicherbarrieren für diese Situationen gesehen.

Um ehrlich zu sein, ich verdächtigen es ist in Ordnung. Ich vermute, dass alles, was zwischen solchen Threads koordiniert werden muss (entweder indem man einen neuen erzeugt oder einen Aufruf an einen bestehenden mahnt), eine volle Speicherbarriere auf beiden beteiligten Threads verwendet. Sie haben jedoch absolut recht, sich Sorgen zu machen, und ich hoffe, dass Sie eine eindeutigere Antwort von jemandem bekommen, der schlauer ist als ich. Vielleicht möchten Sie Joe Duffy per E-Mail, seinen Standpunkt zu diesem Thema zu erhalten ...

+0

Yeh, ich vermute das gleiche, aber ich will sicher sein. Ich werde ihm eine E-Mail schicken, danke. –

4

Aber es ist gewährleistet, dass der Haupt-Thread seine Cache-Flush wird und die VariableA Inhalt in den Hauptspeicher schreiben,

Ja, dies wird vom MS CLR-Speichermodell garantiert. Nicht unbedingt für andere Implementierungen des CLI (dh ich bin mir über Mono nicht sicher). Der ECMA-Standard erfordert dies nicht.

so kann der zweite Thread den neu zugewiesenen Wert lesen?

Das setzt voraus, dass der Cache aktualisiert wurde. Es ist wahrscheinlich durch die Erstellung des Threads garantiert (wie Jon Skeet sagte).Es ist jedoch nicht durch den vorherigen Punkt garantiert. Der Cache wird bei jedem Schreibvorgang geleert, nicht jedoch bei jedem Lesevorgang.

Sie könnten sehr sicher mit VolatileRead(ref variableA) machen, aber es wird empfohlen (Jeffrey Richter), die Interlocked Klasse zu verwenden. Beachten Sie, dass VolatileWrite() in MS.NET überflüssig ist.

5

Es gibt viele Orte, an denen implizite Speicherbarrieren entstehen. Dies ist einer von ihnen. Starting Threads schaffen volle Barrieren. So wird der Schreibvorgang auf variableA festgeschrieben, bevor der Thread gestartet wird und die ersten Lesevorgänge aus dem Hauptspeicher abgerufen werden. Natürlich ist dies in Microsofts Implementierung der CLR ein eher strittiger Punkt, da Schreibvorgänge bereits eine flüchtige Semantik aufweisen. Aber die gleiche Garantie wird in der ECMA-Spezifikation nicht gegeben, so dass es theoretisch möglich ist, dass sich die Mono-Implementierung in dieser Hinsicht anders verhalten könnte.

Meine Sorge ist, wenn sichergestellt ist, dass das Thema B VariableA mit Wert 1 ohne Verwendung von flüchtigen sehen?

In diesem Fall ... ja. Wenn Sie jedoch im zweiten Thread weiterhin variableA verwenden, gibt es nach dem ersten Lesen keine Garantie, dass Updates angezeigt werden.

Aber es ist sichergestellt, dass der Haupt Thread seinen Cache-Flush wird und die VariableA Inhalt in den Haupt Speicher schreiben, so dass der zweite Thread der neu zugewiesenen Wert lesen kann?

Ja.

Zusätzlich wird sichergestellt, dass der zweite Thread die VariableA Inhalte aus dem Haupt Speicher gelesen werden?

Ja, aber nur beim ersten Lesen.

Sicher Zugabe volatil der VariableA Erklärung wird der Code richtig machen. Aber, ist es notwendig?

In diesem sehr spezifischen und engen Fall ... nein. Im Allgemeinen wird jedoch empfohlen, in diesen Szenarien das Schlüsselwort volatile zu verwenden. Dadurch wird Ihr Code nicht nur threadsicherer, wenn das Szenario komplizierter wird, sondern es wird auch die Tatsache dokumentiert, dass das Feld von mehr als einem Thread verwendet wird und Sie die Auswirkungen der Verwendung einer Sperre berücksichtigt haben. freie Strategie.

+0

Hallo Brian, "Ja, aber nur beim ersten Lesen." - Könnten Sie das irgendwie rechtfertigen? Hast du ein paar Links? –

Verwandte Themen