PLINQ 中的合併選項
當查詢平行執行時,PLINQ 會分割來源序列,讓多個執行緒可以在不同的組件上同時工作,通常是在個別的執行緒上。 如果結果是在一個執行緒上使用,例如在 foreach
(Visual Basic 中的 For Each
) 迴圈中,則必須將每個執行緒中的結果合併回單一序列。 PLINQ 執行的合併類型,取決於存在查詢中的運算子。 比方說,對結果強制執行新順序的運算子,必須緩衝所有執行緒中的所有元素。 就使用執行緒的角度而言 (也是應用程式使用者的觀點),完整緩衝的查詢在產生其第一個結果前可能會執行不算短的一段時間。 其他運算子預設會部分進行緩衝;會批次產生其結果。 有一個運算子 (ForAll) 預設不會進行緩衝。 它會立即產生所有執行緒中的所有元素。
使用 WithMergeOptions 方法,如下列範例所示,您可以提供提示給 PLINQ 以指示要執行何種合併。
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)
如需完整範例,請參閱如何:在 PLINQ 中指定合併選項。
如果特定查詢無法支援要求的選項,將會忽略此選項。 在多數情況下,您不需為 PLINQ 查詢指定合併選項。 不過在某些情況下,藉由測試及量測,您會發現查詢在非預設模式的執行效能最佳。 這個選項的常見用法是強制區塊合併運算子串流處理其結果,以提供更能有效回應的使用者介面。
ParallelMergeOptions
ParallelMergeOptions 列舉包含下列選項,其中針對支援的查詢圖形,會指定在一個執行緒上使用結果時如何產生最終輸出:
Not Buffered
NotBuffered 選項會讓每個處理過的元素一產生就從每個執行緒傳回。 此行為類似「串流處理」輸出。 如果 AsOrdered 運算子存在查詢中,
NotBuffered
會保留來源元素的順序。 雖然NotBuffered
會在一有可用的結果時就開始產生,但產生所有結果的總時間可能仍會比使用其他任一個合併選項還久。Auto Buffered
AutoBuffered 選項會讓查詢將項目收集至緩衝區中,然後定期將緩衝區內容一次產生至耗用端執行緒中。 這類似以區塊方式產生來源資料,而非使用
NotBuffered
的「串流處理」行為。 相較於NotBuffered
,AutoBuffered
會需要更多時間才能讓第一個元素可用於使用的執行緒上。 緩衝區的大小和確切的產生行為無法設定,而且可能有所不同,這取決於和查詢相關的各種因素。FullyBuffered
FullyBuffered 選項會在任一元素產生前先緩衝整個查詢的輸出。 使用此選項時,會需要更多時間才能讓第一個元素可用於使用的執行緒上,但產生整體結果的速度可能仍比使用其他選項快。
支援合併選項的查詢運算子
下表列出支援所有合併選項模式的運算子及其具體限制。
運算子 | 限制 |
---|---|
AsEnumerable | 無 |
Cast | 無 |
Concat | 只含有陣列或清單來源的非排序查詢。 |
DefaultIfEmpty | 無 |
OfType | 無 |
Reverse | 只含有陣列或清單來源的非排序查詢。 |
Select | 無 |
SelectMany | None |
Skip | None |
Take | None |
Where | 無 |
所有其他的 PLINQ 查詢運算子可能會忽略使用者提供的合併選項。 某些查詢運算子 (例如 Reverse 和 OrderBy) 在所有元素都已產生並重新排序之前無法產生任何元素。 因此,當 ParallelMergeOptions 使用於也包含運算子 (例如 Reverse) 的查詢中時,一直到該運算子已產生其結果後,才會在查詢中套用合併行為。
某些運算子是否可以處理合併選項,取決於來源序列的類型,以及先前是否在查詢中使用了 AsOrdered 運算子。 ForAll 一律為 NotBuffered;它會立即產生其元素。 OrderBy 一律為 FullyBuffered;它必須先排序整個清單後才會產生。