Sincronizzazione dei dati per il multithreading

Quando più thread possono effettuare chiamate alle proprietà e ai metodi di un singolo oggetto, è fondamentale sincronizzare tali chiamate. In caso contrario, un thread potrebbe interrompere l'operazione eseguita da un altro thread e l'oggetto potrebbe essere lasciato in uno stato non valido. Una classe i cui membri sono protetti da tali interruzioni è denominato thread-safe.

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

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

  • Sincronizzazione manuale. È possibile usare gli oggetti di sincronizzazione forniti dalla libreria di classi .NET. Vedere Panoramica delle primitive di sincronizzazione, che include una discussione sulla Monitor classe .

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

  • Classi di raccolta nel namespace System.Collections.Concurrent. Queste classi forniscono operazioni di aggiunta e rimozione sincronizzate predefinite. Per altre informazioni, vedere raccolteThread-Safe.

Common Language Runtime fornisce un modello di thread in cui le classi rientrano in una serie di categorie che possono essere sincronizzate in diversi modi a seconda dei requisiti. Nella tabella seguente viene illustrato il supporto per la sincronizzazione per i campi e i metodi con una determinata categoria di sincronizzazione.

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

Nessuna sincronizzazione

Si tratta dell'impostazione predefinita per gli oggetti . Qualsiasi thread può accedere a qualsiasi metodo o campo in qualsiasi momento. È necessario accedere a questi oggetti solo un thread alla volta.

Sincronizzazione manuale

La libreria di classi .NET fornisce una serie di classi per la sincronizzazione dei thread. Vedere Panoramica delle primitive di sincronizzazione.

Aree del codice sincronizzate

È possibile usare la classe o una parola chiave del Monitor compilatore per sincronizzare blocchi di codice, metodi di istanza e metodi statici. Non è disponibile alcun supporto per i campi statici sincronizzati.

Sia Visual Basic che C# supportano il contrassegno dei blocchi di codice con una determinata parola chiave del linguaggio, 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 fino a quando il blocco non diventa disponibile. Quando il thread esce dal blocco di codice sincronizzato, il blocco viene rilasciato, indipendentemente dal modo in cui il thread esce dal blocco.

Note

In .NET 9 e C# 13 o versione successiva, se la lock destinazione è un'istanza dedicataSystem.Threading.Lock, l'istruzione usa lock per creare un'area Lock.EnterScope() sincronizzata.

Visual Basic non supporta System.Threading.Lock in SyncLock, quindi continuare a usare SyncLock con un tipo di riferimento privato dedicato.

Se la lock destinazione non è un'istanza Lock e per tutti gli SyncLock utilizzi, il compilatore usa Monitor.Enter e Monitor.Exit. A causa di tale implementazione, è comunque possibile usare altri Monitor metodi nell'area sincronizzata.

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

Thread.Interrupt può essere usato per interrompere un thread di operazioni di blocco, ad esempio l'attesa dell'accesso a un'area di codice sincronizzata. Thread.Interrupt viene usato anche per interrompere i thread da operazioni come 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 dell'istanza. Usare invece un oggetto privato. Una classe o un'istanza può essere bloccata da codice diverso dal proprio, causando potenzialmente deadlock o problemi di prestazioni.

Supporto del compilatore

Sia Visual Basic che 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 si verifica un'eccezione nel blocco di codice, il lock acquisito da lock o SyncLock viene rilasciato automaticamente. I compilatori C# e Visual Basic generano un blocco try/finally con Monitor.Enter all'inizio del tentativo e Monitor.Exit nel blocco finally. Se viene sollevata un'eccezione all'interno del blocco lock o SyncLock, il gestore finally viene eseguito per consentire di eseguire le eventuali operazioni di pulizia necessarie.

Contesto sincronizzato

Solo nelle applicazioni di .NET Framework è possibile usare SynchronizationAttribute su qualsiasi ContextBoundObject per sincronizzare tutti i metodi e i campi dell'istanza. Tutti gli oggetti nello stesso dominio di contesto condividono lo stesso blocco. È consentito l'accesso a più thread ai metodi e ai campi, ma è consentito un solo thread contemporaneamente.

Vedere anche