10

Rozważmy następujący przykładowy kod, który tworzy enumerable zbiór liczb całkowitych i przetwarza je równolegle:bezpieczeństwo Temat zwrotu plonu z 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 
    } 
} 

To jest zagwarantowane, że wątków roboczych generowane przez Parallel.ForEach() każdy dostanie inny element lub jest jakiś mechanizm blokujący wokół inkrementacji i zwrotu z i wymagane?

+0

Wystarczy użyć Enumerable.Range tam. –

+1

@newStackExchangeInstance: Myślę, że to tylko próbka iteratora. – Dennis

+0

@newStackExchangeInstance: A w jaki sposób 'Enumerable.Range()' pomaga mi w równoległym przetwarzaniu 'IEnumerable'? –

Odpowiedz

11

Parallel.ForEach<TSource>, gdy TSource jest IEnumerable<T> tworzy partycjonowania dla IEnumerable<T> który zawiera własną wewnętrzny mechanizm blokujący, więc nie trzeba do wykonania jakiegokolwiek wątku bezpieczeństwa w swoim iteracyjnej.

Ilekroć wątek pracownik prosi o kawałek pozycji, partycjoner stworzy wewnętrznego wyliczający, który:

  1. nabywa blokady współdzielonej
  2. iteracji źródła (skąd został lewej) w celu pobrania fragmentu pozycji, zapisanie elementów w prywatnej macierzy powoduje zwolnienie blokady, aby inne żądania fragmentów mogły zostać spełnione.
  3. służy do wątku roboczego z jego prywatnej tablicy.

Jak widać, przebieg przez IEnumerable<T> dla celów partycjonowania jest sekwencyjny (dostęp przez współdzieloną blokadę), a partycje są przetwarzane równolegle.

+0

Znalazłem to przez google, zrobiłem to i otrzymałem wiadomość od VS, że można to zrobić w Parallel.ForEach (...). Czy to możliwe, że program ForEach ma teraz tę funkcję, niezależnie od tego, czy dany typ jest informowany? – Squirrelkiller

+0

@MarlonRegenhardt tak, VS jest poprawny. Ta funkcja uproszczenia nazywa się "wnioskiem o typ". Na przykład, gdy wpiszesz 'Parallel.ForEach (myList, ...)', kompilator C# może "wywnioskować" ogólny parametr typu (''), patrząc na ogólny typ 'myList'. –

2

TPL i PLINQ wykorzystują koncepcję podkładek.

Partycjoner to typ dziedziczyjący Partitioner<TSource> i służy do dzielenia sekwencji źródłowej na części numeryczne (lub partycje). Wbudowane partycje zostały zaprojektowane tak, aby podzielić sekwencję źródłową na partycje nienapiętrzające.

+0

Myślę, że to nie odpowiada na pytanie: czy kod w pytaniu (który, na powierzchni, w ogóle nie korzysta z "Partitioner") jest bezpieczny dla wątków? – svick

+0

@svick: pytanie brzmi: "Czy jest zagwarantowane, że wątki robocze generowane przez Parallel.ForEach() będą miały inny element". Pytanie nie dotyczy bezpieczeństwa wątków iteratora. – Dennis