本随筆續接:.NET 實作并行的幾種方式(二)
在前兩篇随筆中,先後介紹了 Thread 、ThreadPool 、IAsyncResult (即 APM系列) 、Task 、TPL (Task Parallel Library)。
寫到這些筆者突然意識到 還有一個EMP系列沒有寫,在這裡補充一下:
六、 EAP 、EAP中的典型代表是 WebClient:
EAP系列采用 ***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
七、PLINQ 、LINQ的并行版本
1) 首先看一下PLINQ在 MSDN 中的介紹 :
并行 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 中都運作得更快。
2)PLINQ與LINQ的無縫連接配接
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);
3)簡單Demo

/// <summary>
/// PLinq:Linq的并行版本
/// </summary>
public void Demo1()
{
Task.Run(() =>
{
var result = Enumerable.Range(1, 10).AsParallel().Where(e =>
{
SetTip("開始 " + e);
SetTip("休眠 " + e);
Thread.Sleep(1000);
SetTip("結束 " + e);
return e > 5;
});
SetTip("列印結果");
foreach (var item in result)
{
SetTip(item.ToString());
}
SetTip("并行查詢執行完畢");
});
}
PLinq:Linq的并行版本
4)PLINQ與LINQ的微妙關系
在功能上:PLINQ幾乎實作了LINQ的全部功能、且方法名都是一緻的。
在時間性能上:PLINQ will always attempt to execute a query at least as fast as the query would run sequentially.
是以、PLINQ在執行查詢之前會進行一次“預判”,如果發現了某些情況,目前可能會自動轉為順序執行、即LINQ模式。
5)當查詢包含以下情況時,PLINQ将會預設按照順序模式執行
- 包含 Select 子句、已建立索引的 Where 子句、已建立索引的 SelectMany 子句或 ElementAt 子句的查詢(在排序或篩選運算符移除或重新排列了索引後)。
- 包含 Take、TakeWhile、Skip、SkipWhile 運算符并且源序列中的索引未采用原始順序的查詢。
- 包含 Zip 或 SequenceEquals 的查詢,除非其中一個資料源具有按原始順序排列的索引,并且另一個資料源可建立索引 (例如:數組 或 IList(T)).
- 包含 Concat 的查詢,除非将其應用到可建立索引的資料源。
- 包含 Reverse 的查詢,除非應用到可建立索引的資料源。
6) 強制PLINQ并行查詢
我們可以通過 WithExecutionMode<TSource> 運算符,指定 ParallelExecutionMode.ForceParallelism 強制PLINQ并行查詢。
7)ForAll 多線程枚舉
你有可能對“多線程枚舉”這個詞感到莫名其妙,先看圖:
如果你用 foreach、即 IEnumerable 接口枚舉并行查詢結果,其流程圖如第一個所示:即多個線程并行完成後,
還會有一個Merger操作,使結果回到使用該資料的主線程、并将資料合并。
而 ForAll是并行版本的Foreach.
8)其他運算符
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> 用以指定最大可使用的處理器數量。
9)補充:影響PLINQ查詢性能的因素
附,Demo : https://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
參見更多:随筆導讀:同步與異步
(未完待續...)
傳回導讀目錄,閱讀更多随筆
分割線,以下為部落格簽名:
軟體臭蟲情未了
- 編碼一分鐘
- 測試十年功
随筆如有錯誤或不恰當之處、為希望不誤導他人,望大俠們給予批評指正。