Raccolte thread-safe

.NET Framework 4 introduce lo spazio dei nomi System.Collections.Concurrent che include diverse classi di raccolta thread-safe e scalabili. Più thread possono aggiungere o rimuovere elementi da queste raccolte in modo sicuro ed efficiente, senza richiedere una sincronizzazione aggiuntiva nel codice utente. Quando si scrive nuovo codice, usare le classi di raccolta simultanee ogni volta che più thread scriveranno nella raccolta contemporaneamente. Se si prevede di leggere solo da una raccolta condivisa, è possibile usare le classi dello spazio dei nomi System.Collections.Generic. È consigliabile evitare di usare le classi di raccolta 1.0 a meno che non sia necessario definire come destinazione il runtime di .NET Framework versione 1.1 o precedente.

Sincronizzazione dei thread nelle raccolte di .NET Framework 1.0 e 2.0

Le raccolte introdotte in .NET Framework 1.0 sono reperibili nello spazio dei nomi System.Collections. Queste raccolte, incluse le raccolte ArrayList e Hashtable usate normalmente, supportano la thread safety con la proprietà Synchronized, che restituisce un wrapper thread-safe per la raccolta. Il wrapper funziona bloccando l'intera raccolta in ogni operazione di aggiunta o rimozione. Pertanto, ogni thread che tenta di accedere alla raccolta deve attendere il proprio turno per acquisire l'unico blocco. Questa caratteristica non è scalabile e può causare un peggioramento delle prestazioni per le raccolte di grandi dimensioni. Inoltre, la progettazione non è completamente protetta da race condition. Per altre informazioni, vedere la pagina relativa alla sincronizzazione nelle raccolte generiche.

Le classi di raccolta introdotte in .NET Framework 2.0 sono reperibili nello spazio dei nomi System.Collections.Generic. Sono incluse List<T>, Dictionary<TKey,TValue> e così via. che forniscono maggiore indipendenza dai tipi e migliori prestazioni rispetto alle classi di .NET Framework 1.0. Tuttavia, le classi di raccolta di .NET Framework 2.0 non forniscono la sincronizzazione dei thread. Quando gli elementi vengono aggiunti o rimossi contemporaneamente su più thread, la sincronizzazione deve essere gestita dal codice utente.

È quindi consigliabile usare le classi di raccolta simultanea disponibili in .NET Framework 4 perché offrono non solo l'indipendenza dai tipi delle classi di raccolta .NET Framework 2.0, ma anche una thread safety più efficiente e completa rispetto alle raccolte di .NET Framework 1.0.

Blocco con granularità fine e meccanismi senza blocco

Alcuni tipi di raccolta simultanea usano meccanismi di sincronizzazione leggeri, ad esempio SpinLock, SpinWait, SemaphoreSlim e CountdownEvent, che sono una novità di .NET Framework 4. Questi tipi di sincronizzazione usano in genere la rotazione con stato occupato per breve periodi di tempo prima di impostare il thread in uno stato di attesa effettivo. Quando si prevedono tempi di attesa molto brevi, la rotazione è molto meno dispendiosa a livello di elaborazione rispetto all'attesa, che implica una transizione del kernel complessa. Per le classi di raccolta che usano la rotazione, questo livello di efficienza significa che più thread possono aggiungere e rimuovere elementi con una frequenza molto elevata. Per altre informazioni sul confronto tra spin e blocco, vedere SpinLock e SpinWait.

Le classi ConcurrentQueue<T> e ConcurrentStack<T> non usano alcun blocco. Al contrario, si basano sulle operazioni Interlocked per ottenere la thread safety.

Nota

Poiché supportano ICollection, le classi di raccolta simultanee offrono implementazioni per le proprietà IsSynchronized e SyncRoot, anche se queste sono irrilevanti. IsSynchronized restituisce sempre false e SyncRoot è sempre null (Nothing in Visual Basic).

Nella tabella seguente sono elencati i tipi di raccolta dello spazio dei nomi System.Collections.Concurrent.

Type Descrizione
BlockingCollection<T> Offre la funzionalità di delimitazione e blocco per qualsiasi tipo che implementa IProducerConsumerCollection<T>. Per altre informazioni, vedere Panoramica di BlockingCollection.
ConcurrentDictionary<TKey,TValue> Implementazione thread-safe di un dizionario di coppie chiave-valore.
ConcurrentQueue<T> Implementazione thread-safe di una coda FIFO (First-In, First-Out).
ConcurrentStack<T> Implementazione thread-safe di una coda LIFO (Last-In, First-Out).
ConcurrentBag<T> Implementazione thread-safe di una raccolta non ordinata di elementi.
IProducerConsumerCollection<T> Interfaccia che un tipo deve implementare per essere usato in un oggetto BlockingCollection.
Titolo Descrizione
Panoramica di BlockingCollection Descrive la funzionalità fornita dal tipo BlockingCollection<T>.
Procedura: Aggiungere e rimuovere elementi da un oggetto ConcurrentDictionary Descrive come aggiungere e rimuovere elementi da un oggetto ConcurrentDictionary<TKey,TValue>
Procedura: Aggiungere e rimuovere singoli elementi di un oggetto BlockingCollection Descrive come aggiungere e recuperare elementi da una raccolta di blocco senza usare l'enumeratore di sola lettura.
Procedura: Aggiungere funzionalità di delimitazione e blocco a una raccolta Descrive come usare una classe di raccolta come meccanismo di archiviazione sottostante per una raccolta IProducerConsumerCollection<T>.
Procedura: Usare ForEach per rimuovere elementi in un oggetto BlockingCollection Descrive come usare foreach (For Each in Visual Basic) per rimuovere tutti gli elementi in una raccolta di blocco.
Procedura: Usare matrici di raccolte di blocco in una pipeline Descrive come usare più raccolte di blocco contemporaneamente per implementare una pipeline.
Procedura: Creare un pool di oggetti con un oggetto ConcurrentBag Illustra come usare un contenitore simultaneo per migliorare le prestazioni negli scenari in cui è possibile riutilizzare gli oggetti anziché crearne continuamente di nuovi.

Riferimento

System.Collections.Concurrent