2013-02-04 11 views
5

Una delle cose belle di linq era avere infinite fonti di dati elaborate pigramente su richiesta. Ho provato a parallelizzare le mie domande e ho scoperto che il caricamento lento non funzionava. Per esempio ...Come faccio a scaricare lentamente con 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; 
    } 
} 

Questo programma non riesce a produrre alcun risultato, presumibilmente perché ad ogni passo, la sua attesa per tutte le chiamate al generatore asciugare, che naturalmente non è mai. Se estraggo la chiamata "AsParallel", funziona perfettamente. Quindi, come posso ottenere il mio caricamento lazy durante l'utilizzo di PLINQ per migliorare le prestazioni delle mie applicazioni?

risposta

5

Date un'occhiata a MergeOptions

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

Credo che tu stia confondendo due cose diverse. Il problema qui non è il caricamento lazy (cioè il caricamento solo per quanto è necessario), il problema qui è il buffering dell'output (cioè non restituire immediatamente i risultati).

Nel vostro caso, otterrete i vostri risultati alla fine, anche se potrebbe richiedere un po '(per me, richiede qualcosa come 500 risultati per restituire il primo lotto). Il buffering è fatto per motivi di prestazioni, ma nel tuo caso non ha senso. Come indicato correttamente da Ian, è necessario utilizzare .WithMergeOptions(ParallelMergeOptions.NotBuffered) per disabilitare il buffering dell'output.

Ma, per quanto ne so, PLINQ non esegue il caricamento pigro e non c'è modo di cambiarlo. Ciò significa che se il tuo consumatore (nel tuo caso, il ciclo foreach) è troppo lento, PLINQ genererà risultati più velocemente del necessario e si fermerà solo quando finirai di ripetere i risultati. Ciò significa che PLINQ può sprecare tempo e memoria della CPU.

+0

ottimo punto ... il buffering in PLINQ maschera solo i problemi di nessun caricamento lazy. Forse una strada da percorrere è un metodo di estensione che raggruppa i successivi n elementi ed esegue quelli in parallelo, quindi restituisce i risultati. Questo potrebbe produrre un comportamento pseudo-pigro ... – tbischel

+0

@tbischel Sì, qualcosa del genere funzionerebbe. Un'altra opzione sarebbe quella di usare 'BlockingCollection' con il set di' BoundedCapacity'. – svick