共用方式為


PLINQ 中的順序保留

在 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 會針對其餘查詢保留由順序強制運算符所產生之序列的排序。 換句話說,OrderByThenBy 之類的運算符會被視為後面接著呼叫 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 會保證輸出元素會以增加索引的順序出現,但不會保證哪些索引會指派給哪些元素。

另請參閱