Sincronizzazione dei dati per il multithreading

Se più thread sono in grado di effettuare chiamate alle proprietà e ai metodi di un singolo oggetto, è essenziale che tali chiamate siano sincronizzate. In caso contrario un thread potrebbe interrompere le operazioni eseguite da un altro thread e l'oggetto potrebbe rimanere in uno stato non valido. Una classe i cui membri sono protetti da tali interruzioni è detta thread-safe.

.NET offre diverse strategie per sincronizzare l'accesso a membri statici e di istanza:

  • Aree di codice sincronizzate. È possibile usare la classe Monitor o il supporto del compilatore per questa classe per sincronizzare solo il codice di blocco necessario, migliorando le prestazioni.

  • Sincronizzazione manuale. È possibile usare gli oggetti di sincronizzazione della libreria di classi .NET. Vedere la panoramica sulle primitive di sincronizzazione, che include una descrizione della classe Monitor.

  • Contesti sincronizzati. Solo per le applicazioni .NET Framework e Xamarin, è possibile usare SynchronizationAttribute per abilitare la sincronizzazione semplice e automatica per gli oggetti ContextBoundObject.

  • Classi delle raccolte nello spazio dei nomi System.Collections.Concurrent. Queste classi consentono di eseguire operazioni di aggiunta e rimozione incorporate sincronizzate. Per altre informazioni, vedere Raccolte thread-safe.

Common Language Runtime offre un modello di thread in cui le classi rientrano in un numero di categorie che possono essere sincronizzate in molti modi in base ai requisiti. La tabella seguente indica il supporto di sincronizzazione disponibile per campi e metodi con una determinata categoria di sincronizzazione.

Categoria Campi globali Campi statici Metodi statici Campi di istanza Metodi di istanza Blocchi di codice specifici
Nessuna sincronizzazione No No No No No No
Contesto sincronizzato No No No No
Aree di codice sincronizzate No No Solo se contrassegnato No Solo se contrassegnato Solo se contrassegnato
Sincronizzazione manuale Manuale Manuale Manuale Manuale Manuale Manuale

Nessuna sincronizzazione

Questa è l'impostazione predefinita per gli oggetti. Qualsiasi thread può accedere a qualsiasi metodo o campo in qualsiasi momento. Un solo thread alla volta deve accedere a questi oggetti.

Sincronizzazione manuale

La libreria di classi .NET offre diverse classi per la sincronizzazione dei thread. Vedere Cenni preliminari sulle primitive di sincronizzazione.

Aree di codice sincronizzate

È possibile usare la classe Monitor o una parola chiave del compilatore per sincronizzare aree di codice, metodi di istanza e metodi statici. Non esiste un supporto per i campi statici sincronizzati.

Sia Visual Basic che C# supportano il contrassegno delle aree di codice con una parola chiave del linguaggio specifico, l'istruzione lock in C# o l'istruzione SyncLock in Visual Basic. Quando il codice viene eseguito da un thread, viene effettuato un tentativo di acquisire il blocco. Se il blocco è già stato acquisito da un altro thread, il thread si blocca finché il blocco non diventa disponibile. Quando il thread esce dall'area di codice sincronizzata, il blocco viene rilasciato, indipendentemente dal modo in cui il thread esce dall'area.

Nota

Le istruzioni lock e SyncLock vengono implementate usando Monitor.Enter e Monitor.Exit. Pertanto gli altri metodi di Monitor possono essere usati in combinazione all'interno dell'area sincronizzata.

È anche possibile decorare un metodo con MethodImplAttribute con un valore MethodImplOptions.Synchronized, ottenendo lo stesso effetto dell'uso di Monitor o di una delle parole chiave del compilatore per bloccare l'intero corpo del metodo.

Thread.Interrupt può essere usato per interrompere le operazioni di blocco di un thread, ad esempio in attesa dell'accesso a un'area di codice sincronizzata di un thread. Thread.Interrupt viene usato anche per interrompere l'esecuzione dei thread, ad esempio Thread.Sleep.

Importante

Non bloccare il tipo, ovvero typeof(MyType) in C#, GetType(MyType) in Visual Basic o MyType::typeid in C++, per proteggere i metodi static (metodi Shared in Visual Basic). Usare invece un oggetto statico privato. Analogamente, non usare this in C# (Me in Visual Basic) per bloccare i metodi di istanza. Usare invece un oggetto privato. Una classe o istanza può essere bloccata da un codice diverso dal proprio, causando potenzialmente deadlock o problemi di prestazioni.

Supporto del compilatore

Visual Basic e C# supportano una parola chiave del linguaggio che usa Monitor.Enter e Monitor.Exit per bloccare l'oggetto. Visual Basic supporta l'istruzione SyncLock, C# supporta l'istruzione lock.

In entrambi i casi, se viene generata un'eccezione nell'area di codice, il blocco acquisito da lock o SyncLock viene rilasciato automaticamente. I compilatori C# e Visual Basic creano un blocco try/finally con Monitor.Enter all'inizio del blocco try e Monitor.Exit nel blocco finally. Se viene generata un'eccezione all'interno del blocco lock o SyncLock, viene eseguito il gestore finally per consentire l'esecuzione di operazioni di pulizia.

Contesto sincronizzato

Solo nelle applicazioni .NET Framework e Xamarin è possibile usare SynchronizationAttribute in qualsiasi ContextBoundObject per sincronizzare tutti i campi e i metodi di istanza. Tutti gli oggetti nello stesso dominio di contesto condividono lo stesso blocco. Più thread possono accedere ai metodi e ai campi, ma è consentito un singolo thread alla volta.

Vedi anche