Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
.NET Framework 4 ha introdotto cinque tipologie di raccolta appositamente progettate per supportare operazioni di aggiunta e rimozione multithreaded. Per ottenere la thread-safety, questi tipi usano vari tipi di meccanismi efficienti di blocco e sincronizzazione senza blocchi. La sincronizzazione comporta un sovraccarico in un'operazione. La quantità di overhead dipende dal tipo di sincronizzazione usato, dal tipo di operazioni eseguite e da altri fattori, ad esempio il numero di thread che tentano di accedere simultaneamente alla raccolta.
In alcuni scenari, il sovraccarico di sincronizzazione è trascurabile e consente al tipo multithread di offrire prestazioni notevolmente più veloci e scalare molto meglio rispetto all'equivalente non thread-safe quando protetto da un meccanismo di locking esterno. In altri scenari, il sovraccarico può causare l'esecuzione e la scalabilità del tipo thread-safe circa lo stesso o ancora più lentamente rispetto alla versione esternamente bloccata e non thread-safe del tipo.
Le sezioni seguenti forniscono indicazioni generali su quando usare una raccolta thread-safe rispetto all'equivalente non thread-safe con un blocco fornito dall'utente per le operazioni di lettura e scrittura. Poiché le prestazioni possono variare a seconda di molti fattori, le linee guida non sono specifiche e non sono necessariamente valide in tutte le circostanze. Se le prestazioni sono molto importanti, il modo migliore per determinare il tipo di raccolta da usare consiste nel misurare le prestazioni in base alle configurazioni e ai carichi rappresentativi del computer. Questo documento usa le condizioni seguenti:
Scenario producer-consumer puro
Qualsiasi thread specificato aggiunge o rimuove elementi, ma non entrambi.
Scenario misto produttore-consumatore
Qualsiasi thread specificato aggiunge e rimuove elementi.
Speedup
Prestazioni algoritmiche più veloci rispetto a un altro tipo nello stesso scenario.
Scalabilità
Aumento delle prestazioni proporzionale al numero di core nel computer. Un algoritmo che viene ridimensionato esegue più velocemente su otto core rispetto a due core.
Confronto tra ConcurrentQueue(T) e Queue(T)
Negli scenari producer-consumer puri, in cui il tempo di elaborazione per ogni elemento è molto piccolo (poche istruzioni), può System.Collections.Concurrent.ConcurrentQueue<T> offrire modesti vantaggi in termini di prestazioni rispetto a un System.Collections.Generic.Queue<T> che ha un blocco esterno. In questo scenario, ConcurrentQueue<T> offre prestazioni ottimali quando si accoda un thread dedicato e un thread dedicato si occupa dell'estrazione dalla coda. Se non si applica questa regola, è Queue<T> possibile che le prestazioni siano leggermente più veloci rispetto ConcurrentQueue<T> ai computer con più core.
Quando il tempo di elaborazione è di circa 500 FLOPS (operazioni a virgola mobile) o più, la regola a due thread non si applica a ConcurrentQueue<T>, che ha quindi una scalabilità molto buona. Queue<T> non è scalabile correttamente in questo scenario.
Negli scenari produttore-consumatore misti, quando il tempo di elaborazione è molto ridotto, un Queue<T> che ha un blocco esterno è più efficiente rispetto a ConcurrentQueue<T>. Tuttavia, quando la potenza di calcolo è di circa 500 FLOPS o più, la ConcurrentQueue<T> è migliore in termini di scalabilità.
Stack vs. ConcurrentStack
In scenari producer-consumer puri, quando il tempo di elaborazione è molto piccolo, quindi System.Collections.Concurrent.ConcurrentStack<T> e System.Collections.Generic.Stack<T> che hanno un blocco esterno probabilmente si comporteranno in modo simile con un thread di push dedicato e un thread di estrazione dedicato. Tuttavia, man mano che il numero di thread aumenta, entrambi i tipi rallentano a causa di un aumento della contesa e Stack<T> potrebbero migliorare le prestazioni rispetto ConcurrentStack<T>a . Quando il tempo di elaborazione è di circa 500 FLOPS o più, entrambi i tipi vengono ridimensionati a circa la stessa velocità.
Negli scenari producer-consumer misti, ConcurrentStack<T> è più veloce sia per carichi di lavoro di piccole dimensioni che di grandi dimensioni.
L'uso di PushRange e TryPopRange può accelerare notevolmente i tempi di accesso.
Confronto tra ConcurrentDictionary e Dictionary
In generale, usare un System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue> in qualsiasi scenario in cui si aggiungono e aggiornano chiavi o valori simultaneamente da più thread. Negli scenari che prevedono aggiornamenti frequenti e relativamente poche letture, in genere offre ConcurrentDictionary<TKey,TValue> vantaggi modesti. Negli scenari che coinvolgono molte letture e molti aggiornamenti, ConcurrentDictionary<TKey,TValue> è generalmente significativamente più veloce su computer con un numero qualsiasi di core.
Negli scenari che prevedono aggiornamenti frequenti, è possibile aumentare il grado di concorrenza in ConcurrentDictionary<TKey,TValue> e quindi misurare se le prestazioni aumentano nei computer con più core. Se si modifica il livello di concorrenza, evitare le operazioni globali il più possibile.
Se si legge solo la chiave o i valori, Dictionary<TKey,TValue> è più veloce perché non è necessaria alcuna sincronizzazione se il dizionario non viene modificato da alcun thread.
ConcurrentBag
Negli scenari puro produttore-consumatore, System.Collections.Concurrent.ConcurrentBag<T> probabilmente avrà prestazioni più lente rispetto agli altri tipi di collezioni concorrenti.
Negli scenari producer-consumer misti, ConcurrentBag<T> è in genere molto più veloce e scalabile rispetto a qualsiasi altro tipo di raccolta simultaneo per carichi di lavoro di grandi dimensioni e di piccole dimensioni.
BlockingCollection
Quando sono necessarie le semantiche di delimitazione e blocco, System.Collections.Concurrent.BlockingCollection<T> probabilmente si eseguirà più velocemente di qualsiasi implementazione personalizzata. Supporta anche l'annullamento avanzato, l'enumerazione e la gestione delle eccezioni.