Elenco sicurezza thread

Sto usando il codice qui sotto

var processed = new List(); Parallel.ForEach(items, item => { processed.Add(SomeProcessingFunc(item)); }); 

Il thread del codice sopra è sicuro? C’è una possibilità che l’elenco elaborato venga danneggiato? O dovrei usare un lucchetto prima di aggiungere?

 var processed = new List(); Parallel.ForEach(items, item => { lock(items.SyncRoot) processed.Add(SomeProcessingFunc(item)); }); 

Grazie.

No! Non è affatto sicuro, perché processed.Add non lo è. Puoi fare quanto segue:

 items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList(); 

Tieni presente che Parallel.ForEach stato creato principalmente per operazioni imperative per ciascun elemento della sequenza. Quello che fai è mappare: proietta ogni valore della sequenza. Questo è ciò per cui Select stato creato. AsParallel lo AsParallel attraverso i thread nel modo più efficiente.

Questo codice funziona correttamente:

 var processed = new List(); Parallel.ForEach(items, item => { lock(items.SyncRoot) processed.Add(SomeProcessingFunc(item)); }); 

ma non ha senso in termini di multithreading. lock ad ogni iterazione impone l’esecuzione totalmente sequenziale, un gruppo di thread attenderà il thread singolo.

Uso:

 var processed = new ConcurrentBag(); 

Vedi ciclo foreach parallelo: comportamento strano .

Per citare Jon Skeet prima che arrivi qui:

Come parte di Parellel Extensions in .Net 4, ci sono diverse nuove collezioni in un nuovo spazio System.Collections.Concurrent nomi System.Collections.Concurrent . Questi sono progettati per essere sicuri di fronte alle operazioni concorrenti da più thread, con un blocco relativamente piccolo.

Questi includono IProducerConsumerCollection, BlockingCollection, ConcurrentBag, ConcurrentQueue, ConcurrentStack, and ConcurrentDictionary tra gli altri.

In alternativa alla risposta di Andrey:

 items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList(); 

Potresti anche scrivere

 items.AsParallel().ForAll(item => SomeProcessingFunc(item)); 

Ciò rende la query che è dietro ancora più efficiente perché non è richiesta un’unione, MSDN . Assicurati che la funzione SomeProcessingFunc sia thread-safe. E penso, ma non l’ho testato, che hai ancora bisogno di un lucchetto se l’elenco può essere modificato in un altro thread (aggiungendo o rimuovendo) elementi.

Usando ConcurrentBag di tipo Something

 var bag = new ConcurrentBag>; var items = GetAllItemsINeed(); Parallel.For(items,i => { bag.Add(i.DoSomethingInEachI()); }); 

la lettura è thread-safe, ma l’aggiunta non lo è. È necessario un setup di blocco lettore / scrittore poiché l’aggiunta potrebbe causare il ridimensionamento dell’array interno che potrebbe compromettere una lettura simultanea.

Se è ansible garantire che l’array non verrà ridimensionato in aggiunta, si può essere sicuri di aggiungere durante la lettura, ma non citarlo su questo.

Ma in realtà, una lista è solo un’interfaccia per un array.