在 PLINQ 中,目標是將效能最大化,同時維持正確性。 查詢應該儘快執行,但仍會產生正確的結果。 在某些情況下,正確性需要保留來源序列的順序。然而,保持順序可能在計算上是昂貴的。 因此,根據預設,PLINQ 不會保留來源序列的順序。 在這方面,PLINQ 類似於 LINQ to SQL,但與會保留順序的 LINQ to Objects 不同。
若要覆寫預設行為,您可以使用來源序列上的 AsOrdered 運算符來開啟順序保留。 然後,您可以使用 AsUnordered 方法在查詢語句中關閉順序保留。 使用這兩種方法時,會根據啟發學習法來處理查詢,以判斷要以平行或循序方式執行查詢。 如需詳細資訊,請參閱 瞭解 PLINQ 中的加速。
下列範例顯示未排序的平行查詢,可篩選符合條件的所有元素,而不嘗試以任何方式排序結果。
var cityQuery =
(from city in cities.AsParallel()
where city.Population > 10000
select city).Take(1000);
Dim cityQuery = From city In cities.AsParallel()
Where city.Population > 10000
Take (1000)
此查詢不一定會在符合條件的來源序列中產生前 1000 個城市,而是一組符合條件的 1000 個城市。 PLINQ 查詢運算符會將來源序列分割成多個做為並行工作的子序列。 如果未指定順序保留,則每個分割區的結果將以任意順序傳遞至查詢的下一個階段。 此外,數據分割可能會在繼續處理其餘元素之前,產生其結果的子集。 每次產生的訂單可能都不同。 您的應用程式無法控制此情況,因為它取決於作業系統排程執行緒的方式。
下列範例會使用 AsOrdered 來源序列上的運算符來覆寫預設行為。 這可確保方法會 Take 傳回來源序列中符合條件的前1000個城市。
var orderedCities =
(from city in cities.AsParallel().AsOrdered()
where city.Population > 10000
select city).Take(1000);
Dim orderedCities = From city In cities.AsParallel().AsOrdered()
Where city.Population > 10000
Take (1000)
不過,此查詢可能不會像未排序的版本一樣快速執行,因為它必須追蹤整個分割區的原始順序,並在合併時確保排序一致。 因此,我們建議您只在需要時使用 AsOrdered ,而且只針對需要它的查詢部分使用。 當不再需要訂單保留時,請使用 AsUnordered 將其關閉。 下列範例會藉由撰寫兩個查詢來達成此目的。
var orderedCities2 =
(from city in cities.AsParallel().AsOrdered()
where city.Population > 10000
select city).Take(1000);
var finalResult =
from city in orderedCities2.AsUnordered()
join p in people.AsParallel()
on city.Name equals p.CityName into details
from c in details
select new
{
city.Name,
Pop = city.Population,
c.Mayor
};
foreach (var city in finalResult) { /*...*/ }
Dim orderedCities2 = From city In cities.AsParallel().AsOrdered()
Where city.Population > 10000
Select city
Take (1000)
Dim finalResult = From city In orderedCities2.AsUnordered()
Join p In people.AsParallel() On city.Name Equals p.CityName
Select New With {.Name = city.Name, .Pop = city.Population, .Mayor = city.Mayor}
For Each city In finalResult
Console.WriteLine(city.Name & ":" & city.Pop & ":" & city.Mayor)
Next
請注意,PLINQ 會針對其餘查詢保留由順序強制運算符所產生之序列的排序。 換句話說,OrderBy 和 ThenBy 之類的運算符會被視為後面接著呼叫 AsOrdered。
查詢運算子和排序
下列查詢運算子會將順序保留引入查詢中的所有後續作業,直到呼叫 AsUnordered 為止:
在某些情況下,下列 PLINQ 查詢運算子可能需要排序的來源序列來產生正確的結果:
某些 PLINQ 查詢運算符的行為會有所不同,視其來源序列是排序還是未排序而定。 下表列出這些運算符。
| 操作員 | 排序來源序列時的結果 | 未排序來源序列時的結果 |
|---|---|---|
| Aggregate | 非結合性或非交換性運算的非確定性輸出 | 非結合性或非交換性運算的非確定性輸出 |
| All | 不適用 | 不適用 |
| Any | 不適用 | 不適用 |
| AsEnumerable | 不適用 | 不適用 |
| Average | 非結合性或非交換性運算的非確定性輸出 | 非結合性或非交換性運算的非確定性輸出 |
| Cast | 已排序的結果 | 未排序的結果 |
| Concat | 已排序的結果 | 未排序的結果 |
| Count | 不適用 | 不適用 |
| DefaultIfEmpty | 不適用 | 不適用 |
| Distinct | 已排序的結果 | 未排序的結果 |
| ElementAt | 傳回指定的元素 | 任意元素 |
| ElementAtOrDefault | 傳回指定的元素 | 任意元素 |
| Except | 未排序的結果 | 未排序的結果 |
| First | 傳回指定的元素 | 任意元素 |
| FirstOrDefault | 傳回指定的元素 | 任意元素 |
| ForAll | 以非決定性方式平行執行 | 以非決定性方式平行執行 |
| GroupBy | 已排序的結果 | 未排序的結果 |
| GroupJoin | 已排序的結果 | 未排序的結果 |
| Intersect | 已排序的結果 | 未排序的結果 |
| Join | 已排序的結果 | 未排序的結果 |
| Last | 傳回指定的元素 | 任意元素 |
| LastOrDefault | 傳回指定的元素 | 任意元素 |
| LongCount | 不適用 | 不適用 |
| Min | 不適用 | 不適用 |
| OrderBy | 重新排序序列 | 啟動新的已排序區段 |
| OrderByDescending | 重新排序序列 | 啟動新的已排序區段 |
| Range | 不適用(與 AsParallel 相同的預設值) | 不適用 |
| Repeat | 不適用(與 AsParallel相同的預設值) | 不適用 |
| Reverse | 逆轉 | 不執行任何動作 |
| Select | 已排序的結果 | 未排序的結果 |
| Select (索引) | 已排序的結果 | 未排序的結果。 |
| SelectMany | 已排序的結果。 | 未排序的結果 |
| SelectMany (索引) | 已排序的結果。 | 未排序的結果。 |
| SequenceEqual | 有序比較 | 未排序的比較 |
| Single | 不適用 | 不適用 |
| SingleOrDefault | 不適用 | 不適用 |
| Skip | 略過前 n 個元素 | 略過任何 n 元素 |
| SkipWhile | 已排序的結果。 | 不確定性。 在目前的任意順序上執行 SkipWhile 操作 |
| Sum | 非結合性或非交換性運算的非確定性輸出 | 非結合性或非交換性運算的非確定性輸出 |
| Take | 取得第一個元素n |
接受任何 n 元素 |
| TakeWhile | 已排序的結果 | 不確定性。 在當前的任意順序中執行 TakeWhile |
| ThenBy | 補充 OrderBy |
補充 OrderBy |
| ThenByDescending | 補充 OrderBy |
補充 OrderBy |
| ToArray | 已排序的結果 | 未排序的結果 |
| ToDictionary | 不適用 | 不適用 |
| ToList | 已排序的結果 | 未排序的結果 |
| ToLookup | 已排序的結果 | 未排序的結果 |
| Union | 已排序的結果 | 未排序的結果 |
| Where | 已排序的結果 | 未排序的結果 |
| Where (索引) | 已排序的結果 | 未排序的結果 |
| Zip | 已排序的結果 | 未排序的結果 |
未排序的結果不會主動洗牌,它們只是沒有任何特殊的排序邏輯。 在某些情況下,未排序的查詢可能會保留來源序列的順序。 對於使用索引選取運算子的查詢,PLINQ 會保證輸出元素會以增加索引的順序出現,但不會保證哪些索引會指派給哪些元素。