Lock o Monitor.Enter?
In questi giorni ho avuto una pacifica(!?!?) discussione in privato sull'uso dei lock. Il Framework mette a disposizione varie classi per questa importante necessità. La più semplice è con l'uso del SyncLock di Visual Basic.Net (Lock {} in C#) o con la classe Monitor presente in System.Threading. Per vedere le differenze scriviamo due classi, la prima che utilizza il SyncLock di VB, la seconda direttamente la classe Monitor:
public class WithLock public Conteggio as integer=0 public sub Aumenta() SyncLock me conteggio+=1 end synclock end sub end class<p />public class WithNoLock public Conteggio as integer=0 public sub Aumenta() Monitor.Enter(me) conteggio+=1 Monitor.Exit(me) end sub end class
Ora a apriamo il Reflecton per vedere nella compilazione che cosa ha creato il Framework:
Puvlic class WithLock
Public Sub Aumenta() Dim lock1 As WithLock = Me Monitor.Enter(lock1) Try Me.Conteggio = (Me.Conteggio + 1) Return Finally Monitor.Exit(lock1) End Try End Sub End Class
Public Class WithNoLock
Public Sub Aumenta() Monitor.Enter(Me) Me.Conteggio = (Me.Conteggio + 1) Monitor.Exit(Me) End Sub End Class
Come si può vedere il Framework ha modificato la funzione del Visual Basic SyncLock nell'equivalente Monitor.Enter... Non c'è alcuna differenza dunque... be', una evidente c'è: nel caso dell'utilizzo della funzione SyncLock il framework ci garanrisce che il lock venga tolto alla fine del'elaborazione della funzione anche in presenza di errori nel blocco del codice all'interno del lock. Infatti, con l'utilizzo della classe Monitor si deve sempre essere certi di sbloccare il lock creato con l'Enter, altrimenti quella risorsa rimarrà bloccata in eterno, e il modo utilizzato dal Framework - l'inserimento della linea "Monitor.Exit()" nel blocco Finally ci garantisce questo.
Dunque sembra che il modo migliore sia l'utilizzo degli appositi comandi SyncLock e Lock {} di Visual Basic e C#. Ma è realmente così?
Nei casi reali, quante volte viene lasciato un blocco di codice importante - in cui si desidera utilizzare addirittura il lock delle risorse - senza una gestione personale degli errori? Rivediamo il codice d'esempio modificandolo con qualcosa di reale:
public class WithLock2 public Conteggio as integer=0 public sub Aumenta() try SyncLock me conteggio+=1 end synclock catch ' .... end try end sub end class<p />public class WithNoLock2 public Conteggio as integer=0 public sub Aumenta() try Monitor.Enter(me) conteggio+=1 catch ' .... finally Monitor.Exit(me) end try end sub end class
Vediamo ora il codice prodotto dal Framework:
Public Class WithLock2 Public Sub Aumenta() Dim lock1 As WithLock2 Try lock1 = Me Monitor.Enter(lock1) Try Me.Conteggio = (Me.Conteggio + 1) Return Finally Monitor.Exit(lock1) End Try Return Catch Exception exception1 ProjectData.SetProjectError(exception1) ProjectData.ClearProjectError Return End Try End Sub End Class
Addiriturra sono presenti due blocchi try... mentre nella versione il con Monitor nativo:
Public Class WithNoLock2 Public Sub Aumenta() Try Monitor.Enter(Me) Me.Conteggio = (Me.Conteggio + 1) Return Catch Exception exception1 ProjectData.SetProjectError(exception1) ProjectData.ClearProjectError Return Finally Monitor.Exit(Me) End Try End Sub End Class
E' restata immutata, più veloce e più leggibile. Quali sono dunque le conclusioni di questo blog? Boh! Chi aveva ragione? Mah... :)
Uffa... non c'è neanche la tappa del giro d'Italia oggi...











