2013-02-04 11 views
5

Jedną z miłych rzeczy o linq było posiadanie nieskończonych źródeł danych przetwarzanych leniwie na żądanie. Próbowałem zsynchronizować moje zapytania i okazało się, że leniwe ładowanie nie działa. Na przykład ...Jak uzyskać leniwy załadunek za pomocą PLINQ?

class Program 
{ 
    static void Main(string[] args) 
    { 
     var source = Generator(); 
     var next = source.AsParallel().Select(i => ExpensiveCall(i)); 
     foreach (var i in next) 
     { 
      System.Console.WriteLine(i); 
     } 
    } 

    public static IEnumerable<int> Generator() 
    { 
     int i = 0; 
     while (true) 
     { 
      yield return i; 
      i++; 
     } 
    } 

    public static int ExpensiveCall(int arg) 
    { 
     System.Threading.Thread.Sleep(5000); 
     return arg*arg; 
    } 
} 

Ten program nie przynosi żadnych rezultatów, przypuszczalnie dlatego, że na każdym kroku, jego oczekiwania dla wszystkich połączeń z generatora wysychać, co oczywiście nie jest. Jeśli wezmę połączenie "AsParallel", działa dobrze. Jak mogę uzyskać ładną, leniwą ładowanie podczas korzystania z PLINQ, aby poprawić wydajność moich aplikacji?

Odpowiedz

5

Spójrz na MergeOptions

var next = source.AsParallel() 
       .WithMergeOptions(ParallelMergeOptions.NotBuffered) 
       .Select(i => ExpensiveCall(i)); 
2

myślę, że jesteś mylące dwie różne rzeczy. Problemem tutaj nie jest leniwy ładunek (to jest ładowanie tylko tyle, ile jest konieczne), problemem tutaj jest buforowanie wyjściowe (to jest niezwracanie wyników natychmiast).

W twoim przypadku otrzymasz wyniki w końcu, chociaż może to trochę potrwać (dla mnie wymaga to około 500 wyników, aby zwrócić pierwszą partię). Buforowanie odbywa się ze względu na wydajność, ale w twoim przypadku to nie ma sensu. Jak słusznie zauważył Ian, powinieneś użyć .WithMergeOptions(ParallelMergeOptions.NotBuffered), aby wyłączyć buforowanie wyjściowe.

Ale, o ile wiem, PLINQ nie robi leniwego ładowania i nie ma sposobu, aby to zmienić. Oznacza to, że jeśli twój klient (w twoim przypadku, pętla foreach) jest zbyt wolny, PLINQ wygeneruje wyniki szybciej niż to konieczne i zatrzyma się dopiero po zakończeniu iterowania wyników. Oznacza to, że PLINQ może marnować czas procesora i pamięć.

+0

Bardzo dobry punkt ... buforowanie w PLINQ tylko maskuje problemy z brakiem leniwego ładowania. Być może sposobem na przejście jest metoda rozszerzenia, która ładuje następne n elementów i wykonuje je równolegle, a następnie zwraca wyniki. To mogłoby wywołać pseudo leniwą reakcję ... – tbischel

+0

@tbischel Tak, coś takiego by działało. Inną opcją byłoby użycie 'BlockingCollection' z ustawieniem' BoundedCapacity'. – svick

Powiązane problemy