C # -Version des synchronisierten Java-Schlüsselworts?

313

Hat c # eine eigene Version des Java-Schlüsselworts "synchronized"?

Dh in Java kann es entweder für eine Funktion, ein Objekt oder einen Codeblock angegeben werden, wie folgt:

public synchronized void doImportantStuff() {
   // dangerous code goes here.
}

oder

public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}
Soraz
quelle
3
Das Blockformular erfordert einen Verweis zum Sperren. In der Methodenform ist das Sperrobjekt implizit dies (oder die Klasse [this.class, nicht getClass ()] für statische Methoden, aber keine Sperren für Klassen).
Tom Hawtin - Tackline
1
Immer noch nicht geschützt? Ich komme immer hierher, weil ich mich nicht an diese [MethodImpl(MethodImplOptions.Synchronized)]Zeile erinnern kann .
Bitterblue
1
Ich denke, Ihr 2. Snippet würde nicht kompiliert werden - es muss auf etwas synchronisiert werden .
PoweredByRice

Antworten:

466

Erstens: Die meisten Klassen müssen niemals threadsicher sein. Verwenden Sie YAGNI : Wenden Sie Thread-Sicherheit nur an, wenn Sie wissen, dass Sie es tatsächlich verwenden werden (und testen Sie es).

Für die Sachen auf Methodenebene gibt es [MethodImpl]:

[MethodImpl(MethodImplOptions.Synchronized)]
public void SomeMethod() {/* code */}

Dies kann auch für Accessoren (Eigenschaften und Ereignisse) verwendet werden:

private int i;
public int SomeProperty
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    get { return i; }
    [MethodImpl(MethodImplOptions.Synchronized)]
    set { i = value; }
}

Beachten Sie, dass feldartige Ereignisse werden standardmäßig synchronisiert, während automatisch implementierte Eigenschaften sind nicht :

public int SomeProperty {get;set;} // not synchronized
public event EventHandler SomeEvent; // synchronized

Persönlich mag ich die Implementierung nicht, MethodImplda sie sperrt thisoder typeof(Foo)- was gegen Best Practice verstößt. Die bevorzugte Option ist die Verwendung eigener Schlösser:

private readonly object syncLock = new object();
public void SomeMethod() {
    lock(syncLock) { /* code */ }
}

Beachten Sie, dass bei feldähnlichen Ereignissen die Sperrimplementierung vom Compiler abhängt. In älteren Microsoft-Compilern ist es ein lock(this)/ lock(Type)- in neueren Compilern werden jedochInterlocked Updates verwendet - also threadsicher ohne die unangenehmen Teile.

Dies ermöglicht eine detailliertere Verwendung und die Verwendung von Monitor.Wait/ Monitor.Pulseetc für die Kommunikation zwischen Threads.

Ein verwandter Blogeintrag (später überarbeitet ).

Marc Gravell
quelle
@earcam und deine Frage ist? Diese Aussage ist wahr. Die überwiegende Mehrheit der Klassen haben keine Anforderung Thread-sicher zu sein, wird nicht für Thread-Sicherheit getestet und mit Thread-Sicherheit wird die Leistung auswirken. Die Anzahl der Typen, die sich wirklich um Threads kümmern müssen, ist sehr gering - absichtlich synchronisierte Sammlungen, Multiplexer usw.
Marc Gravell
4
Ich denke, ich hätte einfach sagen sollen; "Die meisten Klassen müssen niemals threadsicher sein", aber "alle Entwickler müssen sich der Parallelität bewusst sein". Rückblickend stimme ich zu, dass die Anzahl sehr gering ist (und definitiv etwas, das Sie einmal an einem Ort richtig machen möchten, damit die Mehrheit der Klassen ohne Rücksicht auf ihre Umgebung mit mehreren Threads interagieren kann). Ich wünschte, ich hätte den Kommentar schneller gelöscht =)
earcam
6
In Marc's verlinktem Blog-Post heißt es im März 2010 , dass in .NET 4.0 MethodImplund feldähnliche Ereignisse jetzt einen guten Synchronisationscode generieren und es nicht mehr erforderlich ist, eigene Sperren zu verwenden.
Rory O'Kane
2
Ein Großteil der Anwendungen ist heutzutage webbasiert und wird mit Frameworks bereitgestellt, die auf einer starken Wiederverwendung von Instanzen und einem komplexen Objektlebenszyklus über Abhängigkeitsinjektion beruhen. Die heutzutage übliche Standardeinstellung tendiert dazu, sich auf die Seite der Thread-Sicherheit zu stellen.
Sheepy
1
@Elazar jetzt zu spät, aber fürs Protokoll: Das Ändern des Frameworks ist eine einzeilige Änderung an den csproj, wenn Sie es mit den .net-Standard- / .net-Kernvorlagen erstellt haben - und reguläres .net ist verfügbar, ebenso wie Multi -targeting. Das IDE-Tool ist jedoch einfach schrecklich - Sie müssen nur wissen, was Sie ändern können und woran :)
Marc Gravell
57
static object Lock = new object();

lock (Lock) 
{
// do stuff
}
Jan Gressmann
quelle
9
Möchten Sie Ihr Sperrobjekt wirklich als statisch deklarieren?
Serg10
21
Sicher, so kann jeder Thread problemlos darauf zugreifen, ohne Referenzen weiterzugeben.
Jan Gressmann
32
Wenn wir uns im Kontext der Frage des Fragestellers befinden, sprechen wir über Instanzmethoden. Die Verwendung von static bedeutet, dass der zweite Aufruf blockiert, wenn Thread 1 instance1.DoSomething () und Thread 2 instance2.DoSomething aufruft, obwohl es sich um ein völlig anderes Objekt handelt. Der Aufruf von thread2 sollte nicht blockiert werden, es sei denn, jemand ruft DoSomething für dasselbe Objekt auf . Sie sagen nicht, dass Sie sich irren, aber es ist wichtig, die Auswirkungen der Verwendung von Statik hier zu verstehen, da dies zu einer schlechten Leistung führen kann, wenn Sie global und nicht pro Instanz blockieren.
AaronLS
1
@AaronLS Die statische Sperre ist sehr nützlich, wenn Ihr Objekt Aktionen in einem größeren Bereich als sich selbst ausführt. Kommt zum Beispiel immer bei Webdiensten vor.
Thibault D.
3
-1, da dies ein anderes Verhalten ist, als das OP verlangt. Dies ist eine Klassensperre, keine Instanzsperre.
Tster
39

Hat c # eine eigene Version des Java-Schlüsselworts "synchronized"?

Nein. In C # geben Sie explizit lockRessourcen an, an denen Sie synchron über asynchrone Threads arbeiten möchten. locköffnet einen Block; es funktioniert nicht auf Methodenebene.

Allerdings ist der zugrunde liegende Mechanismus , da ähnliche lockArbeiten durch den Aufruf Monitor.Enter(und später Monitor.Exit) auf der Laufzeit. Java funktioniert laut Sun-Dokumentation genauso .

Konrad Rudolph
quelle
3
Es hat kein äquivalentes "Schlüsselwort", aber wie die obige Antwort von Marc Gravell zeigt, können Sie auf Methodenebene mit der Annotation [MethodImpl (MethodImplOptions.Synchronized)] synchronisieren.
MindJuice
1
Da die synchronizedOn-Methode von Java im Grunde genommen synchronized (this.getClass())nicht ähnlich zu C # ist lock(typeof(this))?
Sri Harsha Chilakapati
2
@SriHarshaChilakapati Das ist nur teilweise richtig. Das synchronizedSchlüsselwort von Java für eine Methode ist eher wie: synchronized(this)Nur für eine statische Methode verhält es sich so synchronized(class).
Bvdb
7

Beachten Sie, dass bei vollen Pfaden die Linie [MethodImpl(MethodImplOptions.Synchronized)]wie folgt aussehen sollte

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]

Traubenfuchs
quelle
1
oder Sie können einfach verwendenusing System.Runtime.CompilerServices;
aloisdg Umzug zu codidact.com
Ich habe diesen Kommentar geschrieben, als ich noch nicht wusste, wie man automatisch Anweisungen einfügt, nachdem ich C # nicht länger als ein paar Tage oder Wochen programmiert hatte, und ich bin erstaunt über diese 3 positiven Stimmen.
Traubenfuchs
1
Du hast mindestens 3 Entwicklern geholfen und das ist schön :)
Aloisdg wechselt zu codidact.com
5

Sie können lockstattdessen die Anweisung verwenden. Ich denke, dies kann nur die zweite Version ersetzen. Denken Sie auch daran, dass beide synchronizedund lockein Objekt bearbeiten müssen.

James
quelle