Come funziona esattamente il blocco?


527

Vedo che per l'utilizzo di oggetti che non sono thread-safe, avvolgiamo il codice con un lucchetto come questo:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

Quindi cosa succede quando più thread accedono allo stesso codice (supponiamo che sia in esecuzione in un'applicazione web ASP.NET). Sono in coda? In tal caso, quanto tempo aspetteranno?

Qual è l'impatto sulle prestazioni a causa dell'utilizzo dei blocchi?


Risposte:


448

La lockdichiarazione è tradotta da C # 3.0 nel seguente:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

In C # 4.0 questo è cambiato e ora viene generato come segue:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    // body
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(temp); 
    }
}

Puoi trovare maggiori informazioni su cosa Monitor.Enterfa qui . Per citare MSDN:

Utilizzare Enterper acquisire il monitor sull'oggetto passato come parametro. Se un altro thread ha eseguito un Enter oggetto sull'oggetto ma non ha ancora eseguito il corrispondente Exit, il thread corrente si bloccherà fino a quando l'altro thread non rilascia l'oggetto. È legale che lo stesso thread invochi Enterpiù di una volta senza bloccarlo; tuttavia, è Exitnecessario richiamare un numero uguale di chiamate prima che altri thread in attesa sull'oggetto si sblocchino.

Il Monitor.Entermetodo attenderà all'infinito; essa non timeout.


15
Secondo MSDN "L'uso della parola chiave lock (C #) o SyncLock (Visual Basic) è generalmente preferito all'uso diretto della classe Monitor, sia perché lock o SyncLock è più conciso, sia perché lock o SyncLock assicurano che il monitor sottostante sia rilasciato, anche se il codice protetto genera un'eccezione. Ciò si ottiene con la parola chiave finally, che esegue il blocco di codice associato indipendentemente dal fatto che venga generata un'eccezione. " msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom

10
Qual è il punto di var temp = obj; linea. dal momento che è solo un riferimento, a che serve farne un altro?
priehl,

11
@priehl Permette all'utente di cambiare objsenza che l'intero sistema si blocchi.
Steven,

7
@Joymon alla fine, ogni caratteristica della lingua è lo zucchero sintattico. Le funzionalità del linguaggio riguardano il rendere gli sviluppatori più produttivi e rendere le applicazioni più gestibili, così come la funzione di blocco.
Steven

2
Corretta. Questo è l'intero scopo di lock-statement e Monitor: in modo da poter eseguire un'operazione in un thread senza doversi preoccupare di un altro thread che lo muck.
Dizzy H. Muffin,

285

È più semplice di quanto pensi.

Secondo Microsoft : la lockparola chiave garantisce che un thread non inserisca una sezione critica del codice mentre un altro thread si trova nella sezione critica. Se un altro thread tenta di inserire un codice bloccato, attenderà, bloccherà, fino al rilascio dell'oggetto.

La lockparola chiave chiama Enterall'inizio del blocco e Exitalla fine del blocco. lockla parola chiave gestisce effettivamente la Monitorclasse nel back-end.

Per esempio:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

Nel codice sopra, prima il thread entra in una sezione critica e poi si bloccherà obj. Quando un altro thread tenta di entrare, tenterà anche di bloccare obj, che è già bloccato dal primo thread. Il secondo thread dovrà attendere il rilascio del primo thread obj. Quando il primo thread lascia, quindi un altro thread si bloccherà obje entrerà nella sezione critica.


9
dovremmo creare un oggetto fittizio da bloccare o possiamo bloccare una variabile esistente nel contesto?
Batmaci,

9
@batmaci - Il blocco su un oggetto fittizio privato separato ti garantisce che nessun altro sta bloccando quell'oggetto. Se blocchi i dati e lo stesso dato è visibile all'esterno, perdi quella garanzia.
Umar Abbas,

8
Cosa succede se più di un processo è in attesa del rilascio del blocco? I processi di attesa sono in coda in modo da bloccare la sezione critica nell'ordine FIFO?
jstuardo,

@jstuardo - Sono in coda, ma l'ordine non è garantito per essere FIFO. Dai
Umar Abbas


47

No, non sono in coda, stanno dormendo

Una dichiarazione di blocco del modulo

lock (x) ... 

dove x è un'espressione di un tipo di riferimento, è esattamente equivalente a

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

Devi solo sapere che si stanno aspettando l'un l'altro, e solo un thread entrerà per bloccare il blocco, gli altri aspetteranno ...

Il monitor è completamente scritto in .net, quindi è abbastanza veloce, guarda anche il monitor di classe con riflettore per maggiori dettagli


6
Si noti che il codice emesso per la lockdichiarazione è leggermente cambiato in C # 4: blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
LukeH

@ArsenMkrt, non sono tenuti in coda "Bloccato". "Penso che ci sia qualche differenza tra lo stato di
sospensione

che differenza intendi @Mohanavel?
Arsen Mkrtchyan,

1
Non era questa la domanda. La domanda riguardava la parola chiave "blocco". Supponiamo che un processo entri in una sezione di "blocco". Ciò significa che il processo blocca quel pezzo di codice e nessun altro processo può essere in grado di accedere a quella sezione fino a quando non viene rilasciato quel blocco. Bene .... ora, altri 2 processi tentano di inserire lo stesso blocco. Poiché è protetto dalla parola chiave "lock", attenderanno, secondo quanto detto in questo forum. Quando il primo processo rilascia il blocco. Quale processo entra nel blocco? il primo che ha tentato di entrare o l'ultimo?
jstuardo,

1
Immagino che intendi il thread anziché il processo ... in tal caso, la risposta è No, non vi è alcuna garanzia che uno entrerà ... altro qui stackoverflow.com/questions/4228864/…
Arsen Mkrtchyan

29

I blocchi impediranno ad altri thread di eseguire il codice contenuto nel blocco. I thread dovranno attendere il completamento del thread all'interno del blocco di blocco e il rilascio del blocco. Ciò ha un impatto negativo sulle prestazioni in un ambiente multithread. Se è necessario, è necessario assicurarsi che il codice all'interno del blocco di blocco sia in grado di elaborare molto rapidamente. Dovresti cercare di evitare attività costose come l'accesso a un database ecc.


11

L'impatto sulle prestazioni dipende dal modo in cui si blocca. Puoi trovare un buon elenco di ottimizzazioni qui: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

Fondamentalmente dovresti provare a bloccare il meno possibile, poiché mette in pausa il tuo codice di attesa. Se hai dei calcoli pesanti o un codice di lunga durata (ad es. Caricamento di file) in un blocco, si verifica una perdita di prestazioni enorme.


1
Ma tentare di scrivere un codice low-lock può spesso portare a bug sottili, difficili da trovare e correggere, anche se sei un esperto nel campo. L'uso di un lucchetto è spesso il minore tra due mali. Dovresti bloccare esattamente quanto è necessario, né più né meno!
Luca,

1
@LukeH: ci sono alcuni schemi di utilizzo in cui il codice low-lock può essere molto semplice e facile [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]. Il pericolo maggiore è che se i requisiti si evolvono oltre ciò che tali tecniche possono essere gestite, potrebbe essere difficile adattare il codice per gestirlo.
supercat

Il link si è rotto.
CarenRose,

8

La parte all'interno dell'istruzione lock può essere eseguita solo da un thread, quindi tutti gli altri thread aspetteranno indefinitamente il thread che tiene il blocco per terminare. Ciò può comportare un cosiddetto deadlock.


8

La lockdichiarazione è tradotta in chiamate ai metodi Entere Exitdi Monitor.

L' lockistruzione attenderà indefinitamente il rilascio dell'oggetto di blocco.


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.