1. Introduzione
La parola chiave lock
contrassegna un blocco di istruzioni come sezione critica ottenendo il lock a mutua esclusione. Lβesempio seguente illustra unβistruzione lock.
La parola chiave lock
impedisce a un thread di entrare in una sezione critica del codice se Γ¨ giΓ presente un altro thread. Se un altro thread tenta di accedere a un codice bloccato, attenderΓ (in stato di blocco) finchΓ© lβoggetto non verrΓ rilasciato.
Tipicamente un istruzione lock
segue questo template:
private readonly static object obj = new Object();
lock (obj)
{
// thread unsafe code
}
che puΓ² essere anche utilizzato black box, senza comprendere il funzionamento interno, ma con la certezza che il codice inserito allβinterno del blocco lock verrΓ acceduto da un thread alla volta, portandolo a diventare thread-safe.
2. La classe Monitor
Il codice indicato sopra viene tradotto dal compilatore di C# 4.0 nel seguente codice:
bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
// body
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(temp);
}
}
Per citare la documentazione ufficiale, il metodo Enter
di Monitor
permette di acquisire lβesclusivitΓ di accesso sullβoggetto passato come parametro.
Se un altro thread ha eseguito un Enter
sullo stesso oggetto in precedenza ma non lo ha ancora rilasciato tramite il metodo Exit
, il thread corrente verrΓ verrΓ fermato fino a che lβaltro thread non rilascerΓ lβoggetto.
Il metodo Monitor.Enter
indica al thread di aspettare anche allβinfinito una risorsa, senza andare mai in timeout.
3. Il parametro in ingresso a lock
Il metodo lock prevede un parametro in ingresso, che spesso viene definito come un new object()
.
Questo perchΓ© lβoggetto che viene passato come parametro serve solo come chiave: se un lock
Γ¨ giΓ stato preso sulla stessa chiave, un ulteriore lock
non puΓ² essere fatto, altrimenti il lock Γ¨ permesso.
Questo Γ¨ il motivo per cui Γ¨ sbagliato usare le stringhe come chiavi del metodo lock, in quanto queste sono oggetti immutabili e inoltre sono accessibili da altre parti dellβapplicazione.
Eβ necessario invece usare una variabile privata generica, come, per esempio, un istanza della classe Object
.
3.1 Non usare lock(this)
Eβ una pratica di cattiva programmazione utilizzare lock(this)
per bloccare lβesecuzione di codice perchΓ© generalmente Γ¨ fuori dal controllo del programmatore chi altro potrebbe stare eseguendo un lock su tale oggetto.
Infatti, ogni codice che possiede un riferimento allβoggetto this
in questione, puΓ² eseguirne un lock senza che il programmatore di tale oggetto ne venga a conoscenza. Questo aumenta la complessitΓ di soluzioni multi-thread e puΓ² modificarne la loro correttezza.