本隨筆續接:.NET 實現並行的幾種方式(二)
在前兩篇隨筆中,先後介紹了 Thread 、ThreadPool 、IAsyncResult (即 APM系列) 、Task 、TPL (Task Parallel Library)。
寫到這些筆者突然意識到 還有一個EMP系列沒有寫,在這裡補充一下:
EPM系列采用 ***Async方法 + ***Completed事件 的編碼規范,不做太多解釋、具體的demo如下:
var address = "http://www.cnblogs.com/08shiyan/"; WebClient client = new WebClient(); Uri uri = new Uri(address); client.DownloadStringCompleted += new DownloadStringCompletedEventHandler((object sender, DownloadStringCompletedEventArgs e) => { this.txtTip.SetTip("下載完成"); }); client.DownloadStringAsync(uri); this.txtTip.SetTip("開始異步下載數據"); WebClient
並行 LINQ (PLINQ) 是 LINQ 模式的並行實現。 PLINQ 查詢在許多方面類似於非並行 LINQ to Objects 查詢。 與順序 LINQ 查詢一樣,PLINQ 查詢對任何內存中 IEnumerable 或 IEnumerable<T> 數據源進行操作,並推遲執行,這意味著在枚舉查詢之前不會開始執行這些操作。 主要區別是 PLINQ 嘗試充分利用系統中的所有處理器。 它利用所有處理器的方法是,將數據源分成片段,然後在多個處理器上對單獨工作線程上的每個片段並行執行查詢。 在許多情況下,並行執行意味著查詢運行速度顯著提高。
通過並行執行,PLINQ 通常只需向數據源添加 AsParallel 查詢操作,即可在某些查詢類型的舊版代碼上獲得顯著的性能改進。 但是,並行可能引入其自己的復雜性,因此並非所有查詢操作在 PLINQ 中都運行得更快。 事實上,並行降低了某些查詢的速度。 因此,您應了解諸如排序等問題如何影響並行查詢。 有關詳細信息,請參閱 Understanding Speedup in PLINQ。
從介紹中、我們可以明確三點:
1、PLINQ是LINQ的並行版本、它擁有和LINQ一樣一樣強大的基因。
2、PLINQ會嘗試充分利用所有處理器、(PLINQ在 MSDN 中的介紹 最多是64個),因此許多情況下PLINQ可以顯著提高的並行效率,尤其是CPU密集型計算的並行率。
3、並非所有查詢操作在 PLINQ 中都運行得更快。
PLINQ 的實現基礎是 :ParallelQuery<TSource>,LINQ的實現基礎是:IEnumerable<TSource>, 當然這裡說的都是泛型版本。
如下的兩個擴展方法、可輕易實現PLINQ和LINQ間的轉化。
public static ParallelQuery<TSource> AsParallel<TSource>(this IEnumerable<TSource> source);
public static IEnumerable<TSource> AsSequential<TSource>(this ParallelQuery<TSource> source);
在功能上:PLINQ幾乎實現了LINQ的全部功能、且方法名都是一致的。
在時間性能上:PLINQ will always attempt to execute a query at least as fast as the query would run sequentially.
因此、PLINQ在執行查詢之前會進行一次“預判”,如果發現了某些情況,當前可能會自動轉為順序執行、即LINQ模式。
包含 Select 子句、已建立索引的 Where 子句、已建立索引的 SelectMany 子句或 ElementAt 子句的查詢(在排序或篩選運算符移除或重新排列了索引後)。
包含 Take、TakeWhile、Skip、SkipWhile 運算符並且源序列中的索引未采用原始順序的查詢。
包含 Zip 或 SequenceEquals 的查詢,除非其中一個數據源具有按原始順序排列的索引,並且另一個數據源可建立索引 (例如:數組 或 IList(T)).
包含 Concat 的查詢,除非將其應用到可建立索引的數據源。
包含 Reverse 的查詢,除非應用到可建立索引的數據源。
我們可以通過 WithExecutionMode<TSource> 運算符,指定 ParallelExecutionMode.ForceParallelism 強制PLINQ並行查詢。
你有可能對“多線程枚舉”這個詞感到莫名其妙,先看圖:
如果你用 foreach、即 IEnumerable 接口枚舉並行查詢結果,其流程圖如第一個所示:即多個線程並行完成後,
還會有一個Merger操作,使結果回到使用該數據的主線程、並將數據合並。
而 ForAll是並行版本的Foreach.
1、AsOrdered<TSource> PLINQ查詢默認是不保留順序的。該運算符可保留源序列的順序(會產生額外的排序開銷、降低性能)。
2、AsUnordered<TSource> 是 AsOrdered<TSource>的反操作、對後續操作不在保持序列,可用於中間查詢、提高性能。
AsUnordered can be called anywhere in the query; it does not need to be called immediately after AsParallel.
3、WithCancellation<TSource> 用於取消操作
4、WithDegreeOfParallelism<TSource> 用以指定最大可使用的處理器數量。
附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
參見更多:隨筆導讀:同步與異步
(未完待續...)