2009-08-25 28 views
6

Für den folgenden Code ich eine Kompilierung Fehlermeldung erhalten, *Warum Sperre (<integer var>) ist nicht erlaubt, aber Monitor.Enter (<integer var>) erlaubt?

‚int‘ ist kein Referenztyp als durch die lock-Anweisung erforderlich

int i = 0; 
lock(i); 

Aber ohne Fehler für diese:

int i = 0; 
Monitor.Enter(i); 

Ich verstehe, dass ein Werttyp sollte nicht zum Sperren aufgrund der Komplikationen du verwendet werden e zum Boxen. Aber warum funktioniert es dann mit Monitor?

Antwort

15

Der Grund, warum diese Sperre ein Sprachenkonstrukt ist und Compiler wählt zusätzliche Semantik auf den Ausdruck. Monitor.Enter ist einfach ein Methodenaufruf und der C# -Compiler führt den Aufruf in keiner Weise speziell durch und daher geht es durch normale Überladungsauflösung und Boxen.

0

Ich würde sagen, es ist, weil Monitor.Enter() ist ein Methodenaufruf, so führt der Compiler das Boxen automatisch, während lock() ist ein syntaktisches Element, so dass der Compiler überprüfen und einen Fehler auf Werttypen werfen kann.

+0

Ich glaube nicht, dass es eine Überprüfung gibt. Ist die Sperre nicht einfach mit einem Monito.Enter in einem try-Block und Monitor.Exit im finally-Block erweitert. – Sandbox

+1

Nein, es ist erweitert, aber zuerst der Compiler-Spots, wenn Sie auf dem Weg albern sind ... – ShuggyCoUk

11

Sie sollten auf keinen Fall Monitor.Enter auf einem int verwenden. Der Grund dafür ist, dass die int Box eingerahmt ist. Wenn Sie also keinen Verweis auf den Boxed-Wert speichern, sperren Sie ein temporäres Objekt, was bedeutet, dass Sie Monitor.Exit nicht aufrufen können, ohne eine Ausnahme zu erhalten.

Die empfohlene Methode zum Sperren ist ein private readonly object zu erstellen und darauf zu sperren. Für eine statische Methode können Sie private static object verwenden.

3

Nur aus Neugier, was machst du mit der Variable 'i', die es gesperrt werden muss? Es kann effizienter sein, die Interlocked-Klasse zu verwenden, wenn Sie alle Ihre Tun einen Zuwachs oder etwas ist:

Interlocked.Increment(i); // i++ in a thread safe manner 

Die Interlocked-Klasse ist das leichteste Gewicht Thread Synch-Tool, das .NET und für einfache Schritte bietet, dekrementiert, liest oder Austausch, es ist die beste Option.

Wenn Sie versuchen, einen Block von Verhalten zu synchronisieren, dann würde ich einfach ein Objekt erstellen, die als Synchronisations Wurzel verwendet werden können:

object syncRoot = new object(); 

// ... 

lock(syncRoot) 
{ 
    // put synced behavior here 
} 
+0

rein aus Interesse ... bin bewusst, Interlocked-Klasse. Vielen Dank. – Sandbox

5

Die Spezifikation für den Compiler definiert the behaviour of lock like so:

Der Kompilierzeittyp des Ausdrucks einer Sperranweisung muss ein Referenztyp oder ein> Typparameter (§25.1.1) sein, der als Referenztyp bekannt ist. Es ist ein Kompilierungsfehler für den Kompilierzeittyp des Ausdrucks, um einen Werttyp zu bezeichnen.

Es definiert dann, was es so lange zu äquivalent ist, wie es

Da-Monitor erstellt.Exit ist nur ein Methodenaufruf ohne irgendwelche Einschränkungen, es verhindert nicht, dass der Compiler automatisch den Int-Befehl eingibt und auf seine fröhliche (und sehr) falsche Art und Weise geht.

lock ist nicht einfach syntaktischer Zucker in der gleichen Weise foreach ist nicht einfach syntaktischer Zucker. Die resultierende IL-Transformation ist nicht präsentiert, um den Rest des Codes, als ob das was geschrieben wurde.

In foreach ist es illegal, die Iterationsvariable zu ändern (obwohl es auf der IL-Ebene im resultierenden Ausgabecode nichts gibt, was dies verhindern würde). In Sperre verhindert der Compiler die Kompilierungszeit bekannter Werttypen, obwohl die resultierende IL sich nicht darum kümmert.

Als Nebenwirkung:
Theoretisch könnte der Compiler mit intimer Kenntnis dieser (und anderen) Methoden ‚gesegnet‘ werden, so dass es offensichtlich Fälle, dass dies geschieht gesichtet, aber im Grunde ist es unmöglich immer diese Stelle an Kompilierzeit (man denke an ein Objekt, das von einer anderen Methode, einer Assembly oder einer Reflektion übergeben wurde), also wäre die Mühe, solche Instanzen zu finden, wahrscheinlich kontraproduktiv.

Je mehr der Compiler über die Interna der API weiß, desto mehr Probleme werden Sie haben, wenn Sie die API in Zukunft ändern wollen.

Es ist zum Beispiel möglich, dass eine Überladung von Monitor.Enter() hinzugefügt werden könnte, die einen Int und einen auf einem Prozess befindlichen Mutex, der dem int-Wert zugeordnet ist, einschloss.
Dies würde den Spezifikationen von Monitor entsprechen (obwohl es wahrscheinlich abscheulich wäre), würde aber massive Probleme für den älteren Compiler verursachen, der immer noch fröhlich eine Operation verhindert, die legal geworden ist.

+0

Schöne Antwort. Vielen Dank. – Sandbox

Verwandte Themen