Opzioni di merge in PLINQ

Quando una query è in esecuzione come parallela, PLINQ partiziona la sequenza di origine in modo che più thread possano operare simultaneamente su parti diverse, in genere su thread separati. Se i risultati devono essere utilizzati in un unico thread, ad esempio in un ciclo foreach (For Each in Visual Basic), i risultati di ogni thread devono essere nuovamente uniti in un'unica sequenza. Il tipo di merge che PLINQ esegue dipende dagli operatori presenti nella query. Ad esempio, gli operatori che impongono un nuovo ordine nei risultati devono memorizzare nel buffer tutti gli elementi da tutti i thread. Dal punto di vista del thread consumer (che è anche quello dell'utente dell'applicazione), una query completamente memorizzata nel buffer potrebbe essere eseguita per un periodo considerevole di tempo prima che produca il primo risultato. Gli altri operatori, per impostazione predefinita, sono parzialmente memorizzati nel buffer e generano i risultati in batch. Un operatore, ForAll, non è memorizzato nel buffer per impostazione predefinita. Genera immediatamente tutti gli elementi di tutti i thread.

Usando il metodo WithMergeOptions, come illustrato nell'esempio seguente, è possibile fornire un hint a PLINQ indicante il tipo di merge da eseguire.

var scanLines = from n in nums.AsParallel()
                    .WithMergeOptions(ParallelMergeOptions.NotBuffered)
                where n % 2 == 0
                select ExpensiveFunc(n);
Dim scanlines = From n In nums.AsParallel().WithMergeOptions(ParallelMergeOptions.NotBuffered)
                Where n Mod 2 = 0
                Select ExpensiveFunc(n)

Per l'esempio completo, vedere Procedura: Specificare le opzioni di Merge in PLINQ.

Se la query specifica non può supportare l'opzione richiesta, l'opzione verrà ignorata. Nella maggior parte dei casi, non è necessario specificare un'opzione di merge per una query PLINQ. In alcuni casi, tuttavia, in base ai test e alle misurazioni, può risultare evidente che una query viene eseguita meglio in una modalità non predefinita. Un uso comune di questa opzione è quello di forzare un operatore di merge in blocchi per trasmettere i risultati e poter fornire un'interfaccia utente più reattiva.

ParallelMergeOptions

L'enumerazione ParallelMergeOptions include le opzioni seguenti che specificano, per le forme di query supportate, come viene generato l'output finale della query quando i risultati vengono utilizzati in un unico thread:

  • Not Buffered

    L'opzione NotBuffered fa in modo che ogni elemento elaborato venga restituito da ogni thread non appena viene prodotto. Questo comportamento è simile alla "trasmissione" dell'output. Se l'operatore AsOrdered è presente nella query, NotBuffered mantiene l'ordine degli elementi di origine. Anche se inizia a produrre risultati non appena sono disponibili, il tempo totale per produrre tutti i risultati potrebbe essere ancora più lungo rispetto all'uso di una delle NotBuffered altre opzioni di unione.

  • Auto Buffered

    Quando si utilizza l'opzione AutoBuffered, tramite la query vengono raccolti gli elementi in un buffer, quindi viene passato periodicamente il contenuto del buffer in un unico blocco al thread consumer. Questo comportamento è simile alla generazione dei dati di origine in "blocchi" più che al comportamento di "trasmissione" di NotBuffered. AutoBuffered potrebbe impiegare più tempo di NotBuffered per rendere disponibile il primo elemento nel thread consumer. Le dimensioni del buffer e l'esatto comportamento di generazione non sono configurabili e possono variare a seconda dei fattori correlati alla query.

  • FullyBuffered

    L'opzione FullyBuffered fa in modo che l'output dell'intera query venga memorizzato nel buffer prima che vengano generati elementi. Quando si usa questa opzione, può essere necessario più tempo prima che il primo elemento sia disponibile nel thread consumer, ma i risultati completi potrebbero tuttavia essere generati più velocemente che usando le altre opzioni.

Operatori di query che supportano le opzioni di merge

La tabella seguente elenca gli operatori che supportano tutte le modalità delle opzioni di merge, soggette alle restrizioni specificate.

Operatore Restrizioni
AsEnumerable Nessuno
Cast Nessuno
Concat Query non ordinate che hanno solo un'origine matrice o elenco.
DefaultIfEmpty Nessuno
OfType Nessuno
Reverse Query non ordinate che hanno solo un'origine matrice o elenco.
Select Nessuno
SelectMany Nessuno
Skip Nessuno
Take Nessuno
Where Nessuno

Tutti gli altri operatori di query PLINQ potrebbero ignorare le opzioni di merge fornito dall'utente. Alcuni operatori di query, ad esempio Reverse e OrderBy, non possono generare elementi finché tutti non sono stati prodotti e riordinati. Quando viene usato ParallelMergeOptions in una query che contiene anche un operatore, ad esempio Reverse, il comportamento di merge non verrà quindi applicato nella query finché tale operatore non avrà prodotto i risultati.

La possibilità di alcuni operatori di gestire le opzioni di merge dipende dal tipo della sequenza di origine e dal fatto che l'operatore AsOrdered sia stato usato in precedenza nella query. ForAll è sempre NotBuffered. Genera immediatamente gli elementi. OrderBy è sempre FullyBuffered. Deve ordinare l'intero elenco prima di generare gli elementi.

Vedi anche