Strutture di dati per la programmazione in parallelo
.NET offre diversi tipi utili nella programmazione parallela, inclusi un set di classi di raccolta simultanee, primitive di sincronizzazione leggera e tipi per l'inizializzazione differita. È possibile usare questi tipi con qualsiasi codice dell'applicazione multithreading, inclusi Task Parallel Library e PLINQ.
Classi di raccolta simultanee
Le classi di raccolta nello spazio dei nomi System.Collections.Concurrent consentono di eseguire operazioni di aggiunta e rimozione thread-safe che, ove possibile, evitano i blocchi e, dove i blocchi sono necessari, usano quelli con granularità fine. Una classe di raccolta simultanea non richiede il codice utente per accettare blocchi quando accede agli elementi. Le classi di raccolta simultanee possono migliorare considerevolmente le prestazioni rispetto a tipi come System.Collections.ArrayList e System.Collections.Generic.List<T> (con il blocco implementato dall'utente) negli scenari in cui più thread aggiungono e rimuovono elementi da una raccolta.
Nella tabella seguente vengono elencate le classi di raccolta simultanee:
Tipo | Descrizione |
---|---|
System.Collections.Concurrent.BlockingCollection<T> | Fornisce funzionalità di blocco e limitazione per le raccolte thread-safe che implementano System.Collections.Concurrent.IProducerConsumerCollection<T>. I thread producer vengono bloccati se non sono disponibili slot o se la raccolta è completa. I thread consumer vengono bloccati se la raccolta è vuota. Questo tipo supporta anche l'accesso che non causa blocchi da parte di consumer e producer. BlockingCollection<T> può essere usata come classe di base o archivio di backup per fornire funzionalità di blocco limitazione per le classi di raccolta che supportano IEnumerable<T>. |
System.Collections.Concurrent.ConcurrentBag<T> | Implementazione di un contenitore thread-safe che fornisce operazioni add e get scalabili. |
System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue> | Tipo di dizionario simultaneo e scalabile. |
System.Collections.Concurrent.ConcurrentQueue<T> | Coda FIFO simultanea e scalabile. |
System.Collections.Concurrent.ConcurrentStack<T> | Stack LIFO simultaneo e scalabile. |
Per altre informazioni, vedere Raccolte thread-safe.
Primitive di sincronizzazione
Le primitive di sincronizzazione nello spazio dei nomi System.Threading consentono la concorrenza con granularità fine e prestazioni più veloci evitando i costosi meccanismi di blocco del codice multithreading legacy.
La tabella seguente elenca i tipi di sincronizzazione:
Tipo | Descrizione |
---|---|
System.Threading.Barrier | Consente a più thread di usare un algoritmo in parallelo fornendo un punto in cui ogni attività può segnalare il proprio arrivo e quindi venire bloccata finché non arrivano alcune o tutte le attività. Per altre informazioni, vedere Barriera. |
System.Threading.CountdownEvent | Semplifica gli scenari di fork e join fornendo un agevole meccanismo di rendezvous. Per altre informazioni, vedere CountdownEvent. |
System.Threading.ManualResetEventSlim | Primitiva di sincronizzazione simile a System.Threading.ManualResetEvent. ManualResetEventSlim è più semplice, ma può essere usata solo per la comunicazione all'interno di un processo. |
System.Threading.SemaphoreSlim | Primitiva di sincronizzazione che limita il numero di thread che possono accedere simultaneamente a una risorsa o a un pool di risorse. Per altre informazioni, vedere Semaphore e SemaphoreSlim. |
System.Threading.SpinLock | Primitiva di blocco a esclusione reciproca che fa in modo che il thread che prova ad acquisire il blocco rimanga in attesa in un ciclo, o spin, per un determinato periodo di tempo prima di sospendere il quantum. Negli scenari in cui si prevede che l'attesa nel blocco sarà breve, SpinLock offre prestazioni migliori di altre forme di blocco. Per altre informazioni, vedere SpinLock. |
System.Threading.SpinWait | Tipo leggero di piccole dimensioni che ruoterà per un periodo di tempo specificato e infine metterà il thread in uno stato di attesa se il numero spin viene superato. Per altre informazioni, vedere SpinWait. |
Per altre informazioni, vedi:
Procedura: Usare SpinLock per la sincronizzazione di basso livello
Procedura: Sincronizzare operazioni simultanee con una barriera.
Classi di inizializzazione differita
Con l'inizializzazione differita, la memoria per un oggetto non viene allocata fino a quando non è necessaria. L'inizializzazione differita può migliorare le prestazioni distribuendo le allocazioni degli oggetti in modo uniforme per l'intera durata di un programma. È possibile abilitare l'inizializzazione differita per qualsiasi tipo personalizzato eseguendo il wrapping del tipo Lazy<T>.
La tabella seguente elenca i nuovi tipi di inizializzazione differita:
Tipo | Descrizione |
---|---|
System.Lazy<T> | Fornisce l'inizializzazione differita leggera e thread-safe. |
System.Threading.ThreadLocal<T> | Fornisce un valore con inizializzazione differita per ogni thread e ogni thread richiama in modo differito la funzione di inizializzazione. |
System.Threading.LazyInitializer | Fornisce metodi statici che evitano di dover allocare un'istanza di inizializzazione differita dedicata e usano invece i riferimenti per garantire che le destinazioni siano state inizializzate quando vi si accede. |
Per altre informazioni, vedere Inizializzazione differita.
Eccezioni di aggregazione
Il tipo System.AggregateException può essere usato per acquisire più eccezioni generate simultaneamente in thread separati e restituirle al thread di unione come un'unica eccezione. I tipi System.Threading.Tasks.Task e System.Threading.Tasks.Parallel e PLINQ usano ampiamente AggregateException a questo scopo. Per altre informazioni, vedere Gestione delle eccezioni e Procedura: Gestire le eccezioni in una query PLINQ.