Versione C # della parola chiave sincronizzata di java?


313

C # ha una propria versione della parola chiave "sincronizzata" java?

Cioè in Java può essere specificato in una funzione, un oggetto o un blocco di codice, in questo modo:

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

o

public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}

3
La forma del blocco richiede un riferimento per il blocco. Nella forma del metodo l'oggetto lock è implicitamente this (o la Class [this.class, non getClass ()] per i metodi statici, ma non bloccare le Classi).
Tom Hawtin - affronta il

1
Non sei ancora protetto? Vengo sempre qui perché non ricordo quella [MethodImpl(MethodImplOptions.Synchronized)]battuta.
Bitterblue,

1
Penso che il tuo secondo frammento non si compili: deve essere sincronizzato su qualcosa.
PoweredByRice

Risposte:


466

Primo: la maggior parte delle classi non dovrà mai essere thread-safe. Usa YAGNI : applica la sicurezza dei thread solo quando sai che lo userai (e testalo).

Per le cose a livello di metodo, c'è [MethodImpl]:

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

Questo può essere utilizzato anche su accessori (proprietà ed eventi):

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

Si noti che gli eventi simili a campi sono sincronizzati per impostazione predefinita, mentre le proprietà implementate automaticamente non sono :

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

Personalmente, non mi piace l'implementazione di MethodImplcome si blocca thiso typeof(Foo)- che è contro le migliori pratiche. L'opzione preferita è usare i tuoi blocchi:

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

Si noti che per eventi di tipo field, l'implementazione del blocco dipende dal compilatore; nei vecchi compilatori Microsoft è un lock(this)/ lock(Type)- tuttavia, nei compilatori più recenti utilizza gliInterlocked aggiornamenti - quindi thread-safe senza le parti cattive.

Ciò consente un utilizzo più granulare e consente di utilizzare Monitor.Wait/ Monitor.Pulseetc per comunicare tra i thread.

Un post di blog correlato (successivamente rivisitato ).


@earcam e la tua domanda è? Questa affermazione è vera. La stragrande maggioranza delle classi hanno alcun requisito di essere thread-safe, non verranno testati per filo di sicurezza, e con filo di sicurezza inciderà sulle prestazioni. Il numero di tipi che devono veramente preoccuparsi dei thread è molto piccolo - raccolte intenzionalmente sincronizzate, multiplexer, ecc.
Marc Gravell

4
Penso che avrei dovuto semplicemente dichiararlo; "la maggior parte delle classi non dovrà mai essere thread-safe" ma "tutti gli sviluppatori devono essere consapevoli della concorrenza". In retrospettiva, sono d'accordo sul fatto che il numero è molto piccolo (e sicuramente qualcosa che vuoi ottenere una volta in un posto, permettendo alla maggior parte delle classi di interagire ignaro del loro ambiente multi-thread). Vorrei aver eliminato il commento quicker =)
earcam

6
Il post sul blog collegato di Marc ha un follow-up di marzo 2010 in cui si afferma che in .NET 4.0, MethodImple gli eventi simili a campi ora generano un buon codice di sincronizzazione e non è più necessario utilizzare i propri blocchi.
Rory O'Kane,

2
Al giorno d'oggi, una buona maggioranza delle applicazioni è basata sul Web, con framework basati su riutilizzo di istanze pesanti e ciclo di vita di oggetti complessi tramite iniezione di dipendenza. La mentalità predefinita in questi giorni tende a errare sul lato della sicurezza dei thread.
Sheepy,

1
@Elazar troppo tardi ora, ma per la cronaca: cambiare il framework è una modifica di una riga a loro csproj se l'hai creato usando i modelli core .net standard / .net - e il normale .net è disponibile, come è multi -targeting. Tuttavia, gli strumenti IDE intorno a questo sono semplicemente terribili - devi solo sapere cosa puoi cambiare e in cosa :)
Marc Gravell

56
static object Lock = new object();

lock (Lock) 
{
// do stuff
}

10
Sei sicuro di voler dichiarare l'oggetto lock come statico ..?
serg10,

21
Certo, così ogni thread può facilmente accedervi senza passare riferimenti in giro.
Jan Gressmann,

33
Se siamo nel contesto della domanda del richiedente, stiamo parlando di metodi di istanza. L'uso di static significa che se il thread 1 chiama instance1.DoSomething () e il thread 2 chiama instance2.DoSomething, la seconda chiamata verrà bloccata anche se si tratta di un oggetto completamente diverso. La chiamata di thread2 non dovrebbe bloccarsi a meno che qualcuno non stia chiamando DoSomething sullo stesso oggetto . Non dire che hai torto, ma dire che è importante capire l'effetto dell'uso statico qui, perché potrebbe causare scarse prestazioni bloccando a livello globale anziché su base per istanza.
AaronLS

1
@AaronLS Il blocco statico è molto utile quando l'oggetto esegue azioni su un ambito più ampio di se stesso. Succede sempre con i servizi web per esempio.
Thibault D.

4
-1 poiché si tratta di un comportamento diverso da quello richiesto dall'OP. Questo è un blocco di classe, non un blocco di istanza.
tster,

39

C # ha una propria versione della parola chiave "sincronizzata" java?

No. In C #, esplicitamente lockrisorse su cui si desidera lavorare in modo sincrono su thread asincroni. lockapre un blocco; non funziona a livello di metodo.

Tuttavia, il meccanismo sottostante è simile poiché lockfunziona invocando Monitor.Enter(e successivamente Monitor.Exit) il runtime. Java funziona allo stesso modo, in base alla documentazione Sun .


3
Non ha una "parola chiave" equivalente, ma come mostra la risposta di Marc Gravell sopra, puoi sincronizzare a livello di metodo usando l'annotazione [MethodImpl (MethodImplOptions.Synchronized)].
MindJuice,

1
Dato che synchronizedil metodo on di Java non è sostanzialmente synchronized (this.getClass())simile su C # lock(typeof(this))?
Sri Harsha Chilakapati,

2
@SriHarshaChilakapati che è solo parzialmente corretto, la synchronizedparola chiave java su un metodo è più simile: synchronized(this)solo su un metodo statico si comporta come synchronized(class).
bvdb,

6

Prendi nota, con percorsi completi la linea: [MethodImpl(MethodImplOptions.Synchronized)]dovrebbe apparire come

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


1
oppure puoi semplicemente usareusing System.Runtime.CompilerServices;
aloisdg passando a codidact.com l'

Ho scritto quel commento quando non sapevo ancora di inserire automaticamente le istruzioni usando, dopo aver programmato C # per non più di qualche giorno o settimana e sono sorpreso di quei 3 voti.
Traubenfuchs,

1
Hai aiutato almeno 3 sviluppatori ed è bello :)
aloisdg si trasferisce su codidact.com l'

5

È possibile utilizzare lockinvece l' istruzione. Penso che questo possa sostituire solo la seconda versione. Inoltre, ricorda che entrambi synchronizede lockdevono operare su un oggetto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.