Quando utilizzare una raccolta thread-safeWhen to Use a Thread-Safe Collection

.NET Framework 4.NET Framework 4 introduce cinque nuovi tipi di raccolta creati specificamente per il supporto di operazioni di aggiunta e rimozione multithread.The .NET Framework 4.NET Framework 4 introduces five new collection types that are specially designed to support multi-threaded add and remove operations. Per ottenere la thread safety, questi nuovi tipi usano vari nuovi meccanismi di sincronizzazione, sia di blocco che senza blocco.To achieve thread-safety, these new types use various kinds of efficient locking and lock-free synchronization mechanisms. La sincronizzazione aggiunge sovraccarico a un'operazione.Synchronization adds overhead to an operation. La quantità di sovraccarico dipende dal tipo di sincronizzazione usato, dal tipo di operazioni eseguite e da altri fattori, quali il numero di thread che provano ad accedere contemporaneamente alla raccolta.The amount of overhead depends on the kind of synchronization that is used, the kind of operations that are performed, and other factors such as the number of threads that are trying to concurrently access the collection.

In determinati scenari il sovraccarico della sincronizzazione è trascurabile e consente al tipo con multithread un'elaborazione molto più rapida e una miglior scalabilità rispetto al tipo equivalente non thread-safe se protetto da un blocco esterno.In some scenarios, synchronization overhead is negligible and enables the multi-threaded type to perform significantly faster and scale far better than its non-thread-safe equivalent when protected by an external lock. In altri scenari il sovraccarico può far sì che la scalabilità e le prestazioni del tipo thread-safe risultino uguali o più lente rispetto alla versione del tipo non thread-safe con blocco esterno.In other scenarios, the overhead can cause the thread-safe type to perform and scale about the same or even more slowly than the externally-locked, non-thread-safe version of the type.

Le sezioni seguenti offrono indicazioni generali su quando usare una raccolta thread-safe o una raccolta non thread-safe equivalente e provvista di un blocco specificato dall'utente per le operazioni di lettura e scrittura.The following sections provide general guidance about when to use a thread-safe collection versus its non-thread-safe equivalent that has a user-provided lock around its read and write operations. Dato che le prestazioni possono variare in base a molti fattori, le indicazioni non sono specifiche e non sono necessariamente valide in qualsiasi circostanza.Because performance may vary depending on many factors, the guidance is not specific and is not necessarily valid in all circumstances. Se le prestazioni sono molto importanti, il modo migliore per determinare il tipo di raccolta da usare è la misurazione delle prestazioni in base alle configurazioni e ai carichi di lavoro di computer campione.If performance is very important, then the best way to determine which collection type to use is to measure performance based on representative computer configurations and loads. In questo documento vengono usati i seguenti termini:This document uses the following terms:

Scenario producer-consumer puroPure producer-consumer scenario
Un qualsiasi thread esegue l'aggiunta o la rimozione di elementi, ma non entrambe le operazioni.Any given thread is either adding or removing elements, but not both.

Scenario producer-consumer mistoMixed producer-consumer scenario
Un qualsiasi thread esegue sia l'aggiunta che la rimozione di elementi.Any given thread is both adding and removing elements.

Aumento della velocitàSpeedup
Prestazioni algoritmiche più veloci rispetto a un altro tipo nello stesso scenario.Faster algorithmic performance relative to another type in the same scenario.

ScalabilitàScalability
Miglioramento delle prestazioni proporzionale al numero di core nel computer.The increase in performance that is proportional to the number of cores on the computer. Un algoritmo con scalabilità viene eseguito più velocemente su otto core che su due core.An algorithm that scales performs faster on eight cores than it does on two cores.

Confronto tra ConcurrentQueue(T) e Queue(T)ConcurrentQueue(T) vs. Queue(T)

Negli scenari producer-consumer puri, in cui il tempo di elaborazione per ogni elemento è molto ridotto (poche istruzioni), System.Collections.Concurrent.ConcurrentQueue<T> offre vantaggi di prestazioni modesti rispetto a System.Collections.Generic.Queue<T> con blocco esterno.In pure producer-consumer scenarios, where the processing time for each element is very small (a few instructions), then System.Collections.Concurrent.ConcurrentQueue<T> can offer modest performance benefits over a System.Collections.Generic.Queue<T> that has an external lock. In questo scenario, ConcurrentQueue<T> offre le prestazioni migliori quando un thread dedicato aggiunge elementi alla coda e un altro thread dedicato rimuove elementi dalla coda.In this scenario, ConcurrentQueue<T> performs best when one dedicated thread is queuing and one dedicated thread is de-queuing. Se non si applica questa regola, le prestazioni di Queue<T> potrebbe persino risultare leggermente più veloci di quelle di ConcurrentQueue<T> nei computer che dispongono di più core.If you do not enforce this rule, then Queue<T> might even perform slightly faster than ConcurrentQueue<T> on computers that have multiple cores.

Quando il tempo di elaborazione è pari o superiore a circa 500 FLOPS (floating point operations, operazioni a virgola mobile), la regola dei due thread non è più valida per ConcurrentQueue<T>, che in questo caso registra una scalabilità ottimale.When processing time is around 500 FLOPS (floating point operations) or more, then the two-thread rule does not apply to ConcurrentQueue<T>, which then has very good scalability. Queue<T> non presenta una scalabilità ottimale in questo scenario.Queue<T> does not scale well in this scenario.

Negli scenari producer-consumer misti, quando il tempo di elaborazione è molto breve, un Queue<T> provvisto di blocco esterno offre una scalabilità migliore rispetto a ConcurrentQueue<T>.In mixed producer-consumer scenarios, when the processing time is very small, a Queue<T> that has an external lock scales better than ConcurrentQueue<T> does. Tuttavia, quando il tempo di elaborazione è pari o superiore a 500 FLOPS, ConcurrentQueue<T> offre una scalabilità migliore.However, when processing time is around 500 FLOPS or more, then the ConcurrentQueue<T> scales better.

Confronto tra ConcurrentStack e StackConcurrentStack vs. Stack

Negli scenari producer-consumer puri, quando il tempo di elaborazione è molto ridotto, è probabile che System.Collections.Concurrent.ConcurrentStack<T> e System.Collections.Generic.Stack<T> con un blocco esterno abbiano prestazioni simili con un thread push dedicato e un thread pop dedicato.In pure producer-consumer scenarios, when processing time is very small, then System.Collections.Concurrent.ConcurrentStack<T> and System.Collections.Generic.Stack<T> that has an external lock will probably perform about the same with one dedicated pushing thread and one dedicated popping thread. Se tuttavia il numero di thread aumenta, le prestazioni di entrambi i tipi rallentano per l'incremento dei conflitti e Stack<T> potrebbe registrare prestazioni migliori di ConcurrentStack<T>.However, as the number of threads increases, both types slow down because of increased contention, and Stack<T> might perform better than ConcurrentStack<T>. Quando il tempo di elaborazione è pari o superiore a 500 FLOPS, la velocità di scalabilità dei due tipi è circa la stessa.When processing time is around 500 FLOPS or more, then both types scale at about the same rate.

Negli scenari producer-consumer misti ConcurrentStack<T> è più veloce per i carichi di lavoro sia piccoli che grandi.In mixed producer-consumer scenarios, ConcurrentStack<T> is faster for both small and large workloads.

L'uso di PushRange e TryPopRange può accelerare notevolmente i tempi di accesso.The use of the PushRange and TryPopRange may greatly speed up access times.

Confronto tra ConcurrentDictionary e DizionarioConcurrentDictionary vs. Dictionary

In generale, è consigliabile usare System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue> negli scenari in cui si aggiungono e aggiornano chiavi o valori da più thread simultaneamente.In general, use a System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue> in any scenario where you are adding and updating keys or values concurrently from multiple threads. Negli scenari che presentano aggiornamenti frequenti e letture poco frequenti, ConcurrentDictionary<TKey,TValue> offre in genere moderati vantaggi.In scenarios that involve frequent updates and relatively few reads, the ConcurrentDictionary<TKey,TValue> generally offers modest benefits. Negli scenari che presentano molte letture e molti aggiornamenti, ConcurrentDictionary<TKey,TValue> è in genere molto più rapida su computer con un numero qualsiasi di core.In scenarios that involve many reads and many updates, the ConcurrentDictionary<TKey,TValue> generally is significantly faster on computers that have any number of cores.

Negli scenari con aggiornamenti frequenti è possibile aumentare il livello di concorrenza di ConcurrentDictionary<TKey,TValue> e quindi effettuare una misurazione per quantificare eventuali incrementi delle prestazioni nei computer con più core.In scenarios that involve frequent updates, you can increase the degree of concurrency in the ConcurrentDictionary<TKey,TValue> and then measure to see whether performance increases on computers that have more cores. Se si modifica il livello di concorrenza, evitare per quanto possibile le operazioni globali.If you change the concurrency level, avoid global operations as much as possible.

Se si esegue solo la lettura di chiavi o valori, Dictionary<TKey,TValue> è più veloce, perché non è richiesta alcuna sincronizzazione se il dizionario non viene modificato da nessun thread.If you are only reading key or values, the Dictionary<TKey,TValue> is faster because no synchronization is required if the dictionary is not being modified by any threads.

ConcurrentBagConcurrentBag

Negli scenari producer-consumer puri, è probabile che l'esecuzione di System.Collections.Concurrent.ConcurrentBag<T> risulti più lenta di quella di altri tipi di raccolta simultanea.In pure producer-consumer scenarios, System.Collections.Concurrent.ConcurrentBag<T> will probably perform more slowly than the other concurrent collection types.

Negli scenari producer-consumer misti, ConcurrentBag<T> è in genere molto più veloce e più scalabile di qualsiasi altro tipo di raccolta simultanea per carichi di lavoro sia piccoli che grandi.In mixed producer-consumer scenarios, ConcurrentBag<T> is generally much faster and more scalable than any other concurrent collection type for both large and small workloads.

BlockingCollectionBlockingCollection

Quando è necessaria la semantica di delimitazione e blocco, è probabile che l'esecuzione di System.Collections.Concurrent.BlockingCollection<T> risulti più rapida di quella di qualsiasi implementazione personalizzata.When bounding and blocking semantics are required, System.Collections.Concurrent.BlockingCollection<T> will probably perform faster than any custom implementation. Supporta anche funzionalità complete di annullamento, enumerazione e gestione eccezioni.It also supports rich cancellation, enumeration, and exception handling.

Vedere ancheSee Also

System.Collections.Concurrent
Raccolte thread-safeThread-Safe Collections
Programmazione parallelaParallel Programming