PLINQ為查詢的平行處理提供了可能性。要平行處理查詢操作只要插入AsParallel方法就可以了。
但是問題就出現在並行本身。也就是說,如果沒有很好的關於同步的設計的話,出來的資料的順序可能是亂的,沒有經過排序的。因為並存執行的時候,可以同時處理很多資料,但並不確保哪些資料是首先處理結束的。所以,象下面的例子中,如果在查詢運算式中插入AsParallel方法,查詢出來的結果有可能會發生改變。
不經過平行處理的查詢操作
using System; using System.Linq; using System.Threading.Tasks; class Program { static void Main(string[] args) { int[] ar = { 1, 2, 3 }; var q1 = from n in ar select n; int sum = 1; foreach (var n in q1) { Console.WriteLine("sum={0}×{1}+{1}", sum, n); sum = sum * n + n; } Console.WriteLine("result {0}", sum); } } |
運行結果
sum=1×1+1 sum=2×2+2 sum=6×3+3 result 21 |
如果象下例那樣在這段代碼的查詢運算式中,加入AsParallel方法,會怎麼樣呢?
在查詢運算式中加入AsParallel方法
var q1 = from n in ar.AsParallel() select n; |
運行結果(結果因作業系統等而不確定,此處是結果的一個舉例。)
sum=1×1+1 sum=2×3+3 sum=9×2+2 result 20 |
這種情況下,雖然計算方法並沒有發生錯誤,但參與計算的數位順序發生了變化,所以計算結果也就不一樣了。為了保證計算內容與計算結果完全一致,需要象下例那樣加上鎖專用對象及鎖語句。
加上鎖專用對象及鎖語句
object o = new object(); foreach (var n in q1) { lock (o) { Console.WriteLine("sum={0}×{1}+{1}", sum, n); sum = sum * n + n; } } |
排序方法為,象下例那樣在方法鏈中加上AsOrdered方法,從而保證順序不變。
排序方法
var q1 = from n in ar.AsParallel().AsOrdered() select n; |
但是,因為要保持順序不變,肯定就意味著會對處理的速度有所影響。如果要使效能發揮到最大,還是要執行跟排序無關的查詢。
另外,“object o = new
object();”這句代碼看起來可能有點奇怪。因為要將object類型進行執行個體化,一般終歸希望執行個體化後的對象是帶有某種功能的。雖然lock語句中如果使用某個具體類型的對象,也可以實現排他性操作,但是如果為瞭解決同步問題,使用object類型的對象是最安全的。
綜上所述,如果涉及到同步與順序問題,濫用PLINQ會容易產生問題。但是如果不涉及到這兩方面,是可以享受到在多核時代因為使用PLINQ而提升效能的好處的。
■ 本章總結
1.任務(Task類)與線程(Thread類)是兩個似是而非的概念。使用Task類是很簡單容易的。
2.可以預先安排任務結束時所要執行的功能。
3.可以簡單地等待多個任務的結束。
4.只要添加AsParallel方法就可以對查詢操作進行平行處理了。
5.PLINQ在同步問題上存在問題,可能不能保持順序不變。
6.為了保持順序不變,需要添加AsOrdered方法,但是對效能產生影響。