10

Si consideri il seguente esempio di codice, che crea una collezione numerabile di numeri interi e lo elabora in parallelo:sicurezza Discussione di rendimento rendimento con Parallel.ForEach()

using System.Collections.Generic; 
using System.Threading.Tasks; 

public class Program 
{ 
    public static void Main() 
    { 
     Parallel.ForEach(CreateItems(100), item => ProcessItem(item)); 
    } 

    private static IEnumerable<int> CreateItems(int count) 
    { 
     for (int i = 0; i < count; i++) 
     { 
      yield return i; 
     } 
    } 

    private static void ProcessItem(int item) 
    { 
     // Do something 
    } 
} 

E 'garantito che i thread di lavoro generati da Parallel.ForEach() Ognuno di essi ha un oggetto diverso o un meccanismo di blocco attorno all'incremento e alla restituzione di i richiesto?

+0

Basta usare Enumerable.Range lì. –

+1

@newStackExchangeInstance: Penso che sia solo un esempio iteratore. – Dennis

+0

@newStackExchangeInstance: E come 'Enumerable.Range()' mi aiuta a elaborare un 'IEnumerable' in parallelo? –

risposta

11

Parallel.ForEach<TSource>, quando TSource è un IEnumerable<T>, crea un partizionamento per il IEnumerable<T> che include la propria meccanismo di bloccaggio interno, quindi non c'è bisogno di realizzare qualsiasi thread-sicurezza nella vostra iteratore.

Ogni volta che un thread di lavoro richiede un pezzo di voci, il partizionamento sarà creare un enumeratore interna, che:

  1. acquisisce un blocco condiviso
  2. itera attraverso la sorgente (da dove è stato lasciato di) per recuperare il blocco di elementi, salvando gli elementi in un array privato
  3. rilascia il blocco in modo che altre richieste di blocco possano essere soddisfatte.
  4. serve il thread di lavoro dal proprio array privato.

Come si vede, la corsa attraverso la IEnumerable<T> ai fini di partizionamento è sequenziale (accessibile tramite un blocco condiviso), e le partizioni vengono elaborati in parallelo.

+0

Trovato tramite Google, lo ha fatto e viene detto da VS che può essere semplicemente filtrato in Parallel.ForEach (...). È possibile che ForEach abbia questa funzionalità a prescindere dal fatto che venga detto a un tipo specifico? – Squirrelkiller

+0

@MarlonRegenhardt sì, VS è corretto. Questa caratteristica di semplificazione è chiamata "inferenza del tipo". Ad esempio, quando si digita 'Parallel.ForEach (myList, ...)', il compilatore C# può "inferire" il parametro di tipo generico ('') osservando il tipo generico di 'myList'. –

2

TPL e PLINQ utilizzano il concetto di divisori .

Il partizionatore è un tipo che eredita Partitioner<TSource> e serve per suddividere la sequenza sorgente in parti (o partizioni) di numeri. I partizionatori integrati sono stati progettati per suddividere la sequenza sorgente in partizioni non sovrapposte.

+0

Penso che questo in realtà non risponda alla domanda: è il codice nella domanda (che, in superficie, non usa affatto 'Partitioner') thread-safe? – svick

+0

@svick: la domanda è "È garantito che i thread di lavoro generati da Parallel.ForEach() ottengano ciascuno un oggetto diverso". La domanda non riguarda la sicurezza del thread dell'iteratore. – Dennis