菜鳥學習並行編程,參考《C#並行編程高級教程.PDF》,如有錯誤,歡迎指正。
目錄
TPL中引入了一個新命名空間System.Threading.Tasks,在該命名空間下Task是主類,表示一個類的異步的並發的操作,創建並行代碼的時候不一定要直接使用Task類,在某些情況下可以直接使用Parallel靜態類(System.Threading.Tasks.Parallel)下所提供的方法,而不用底層的Task實例。
Parallel.Invoke
試圖將很多方法並行運行,如果傳入的是4個方法,則至少需要4個邏輯內核才能足以讓這4個方法並發運行,邏輯內核也稱為硬件線程。
需要注意的是:1.即使擁有4個邏輯內核,也不一定能夠保證所需要運行的4個方法能夠同時啟動運行,如果其中的一個內核處於繁忙狀態,那么底層的調度邏輯可能會延遲某些方法的初始化執行。
2.通過Parallel.Invoke編寫的並發執行代碼一定不能依賴與特定的執行順序,因為它的並發執行順序也是不定的。
3.使用Parallel.Invoke方法一定要測量運行結果、實現加速比以及邏輯內核的使用率,這點很重要。
4.使用Parallel.Invoke,在運行並行方法前都會產生一些額外的開銷,如分配硬件線程等。
好處:這是一種並行運行很多方法的簡單方式,使用Parallel.Invoke,不需要考慮任務和線程的問題。
下面貼代碼:

class Program { private static List<Product> ProductList = null; /* coder:釋迦苦僧 * 沒有特定的執行順序 * 示例中 基於電腦配置 采用了4個方法的並行編程 * Parallel.Invoke 首先會嘗試並行啟動4個方法,充分利用一個或多個物理處理器所提供的多個邏輯內核 * 但是在實際的並行執行中,至少要有4個邏輯內核才能滿足4個方法的並行運行 * 如果有個或者多個邏輯內核處於繁忙狀態,那么底層的調度邏輯可能會延遲某些方法的初始化執行 * 通過Parallel.Invoke編寫的並發執行代碼一定不能依賴與特定的執行順序,因為它的並發執行順序也是不定的。 */ static void Main(string[] args) { ProductList = new List<Product>(); Thread.Sleep(3000); Stopwatch swTask = new Stopwatch(); swTask.Start(); /*執行並行操作*/ Parallel.Invoke(SetProcuct1_500, SetProcuct2_500, SetProcuct3_500, SetProcuct4_500); swTask.Stop(); Console.WriteLine("500條數據 並行編程所耗時間:" + swTask.ElapsedMilliseconds); ProductList = new List<Product>(); Thread.Sleep(3000);/*防止並行操作 與 順序操作沖突*/ Stopwatch sw = new Stopwatch(); sw.Start(); SetProcuct1_500(); SetProcuct2_500(); SetProcuct3_500(); SetProcuct4_500(); sw.Stop(); Console.WriteLine("500條數據 順序編程所耗時間:" + sw.ElapsedMilliseconds); ProductList = new List<Product>(); Thread.Sleep(3000); swTask.Restart(); /*執行並行操作*/ Parallel.Invoke(() => SetProcuct1_10000(), () => SetProcuct2_10000(), () => SetProcuct3_10000(), () => SetProcuct4_10000()); swTask.Stop(); Console.WriteLine("10000條數據 並行編程所耗時間:" + swTask.ElapsedMilliseconds); ProductList = new List<Product>(); Thread.Sleep(3000); sw.Restart(); SetProcuct1_10000(); SetProcuct2_10000(); SetProcuct3_10000(); SetProcuct4_10000(); sw.Stop(); Console.WriteLine("10000條數據 順序編程所耗時間:" + sw.ElapsedMilliseconds); Console.ReadLine(); } private static void SetProcuct1_500() { for (int index = 1; index < 500; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct1 執行完成"); } private static void SetProcuct2_500() { for (int index = 500; index < 1000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct2 執行完成"); } private static void SetProcuct3_500() { for (int index = 1000; index < 2000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct3 執行完成"); } private static void SetProcuct4_500() { for (int index = 2000; index < 3000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct4 執行完成"); } private static void SetProcuct1_10000() { for (int index = 1; index < 20000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct1 執行完成"); } private static void SetProcuct2_10000() { for (int index = 20000; index < 40000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct2 執行完成"); } private static void SetProcuct3_10000() { for (int index = 40000; index < 60000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct3 執行完成"); } private static void SetProcuct4_10000() { for (int index = 60000; index < 80000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct4 執行完成"); } } class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } }
圖中我們可以看出利用 Parallel.Invoke編寫的並發執行代,它的並發執行順序也是不定的。
但是所執行的時間上比不采用並行編程所耗的時間差不多。
這是因為我們在並行編程中操作了共享資源 ProductList ,如果我把代碼做出以下修改,采用並行編程的好處就顯現出來了。

class Program { /* coder:釋迦苦僧 * 沒有特定的執行順序 * 示例中 基於電腦配置 采用了4個方法的並行編程 * Parallel.Invoke 首先會嘗試並行啟動4個方法,充分利用一個或多個物理處理器所提供的多個邏輯內核 * 但是在實際的並行執行中,至少要有4個邏輯內核才能滿足4個方法的並行運行 * 如果有個或者多個邏輯內核處於繁忙狀態,那么底層的調度邏輯可能會延遲某些方法的初始化執行 * 通過Parallel.Invoke編寫的並發執行代碼一定不能依賴與特定的執行順序,因為它的並發執行順序也是不定的。 */ static void Main(string[] args) { Thread.Sleep(3000); Stopwatch swTask = new Stopwatch(); swTask.Start(); /*執行並行操作*/ Parallel.Invoke(SetProcuct1_500, SetProcuct2_500, SetProcuct3_500, SetProcuct4_500); swTask.Stop(); Console.WriteLine("500條數據 並行編程所耗時間:" + swTask.ElapsedMilliseconds); Thread.Sleep(3000);/*防止並行操作 與 順序操作沖突*/ Stopwatch sw = new Stopwatch(); sw.Start(); SetProcuct1_500(); SetProcuct2_500(); SetProcuct3_500(); SetProcuct4_500(); sw.Stop(); Console.WriteLine("500條數據 順序編程所耗時間:" + sw.ElapsedMilliseconds); Thread.Sleep(3000); swTask.Restart(); /*執行並行操作*/ Parallel.Invoke(() => SetProcuct1_10000(), () => SetProcuct2_10000(), () => SetProcuct3_10000(), () => SetProcuct4_10000()); swTask.Stop(); Console.WriteLine("10000條數據 並行編程所耗時間:" + swTask.ElapsedMilliseconds); Thread.Sleep(3000); sw.Restart(); SetProcuct1_10000(); SetProcuct2_10000(); SetProcuct3_10000(); SetProcuct4_10000(); sw.Stop(); Console.WriteLine("10000條數據 順序編程所耗時間:" + sw.ElapsedMilliseconds); Console.ReadLine(); } private static void SetProcuct1_500() { List<Product> ProductList = new List<Product>(); for (int index = 1; index < 500; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct1 執行完成"); } private static void SetProcuct2_500() { List<Product> ProductList = new List<Product>(); for (int index = 500; index < 1000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct2 執行完成"); } private static void SetProcuct3_500() { List<Product> ProductList = new List<Product>(); for (int index = 1000; index < 2000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct3 執行完成"); } private static void SetProcuct4_500() { List<Product> ProductList = new List<Product>(); for (int index = 2000; index < 3000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct4 執行完成"); } private static void SetProcuct1_10000() { List<Product> ProductList = new List<Product>(); for (int index = 1; index < 20000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct1 執行完成"); } private static void SetProcuct2_10000() { List<Product> ProductList = new List<Product>(); for (int index = 20000; index < 40000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct2 執行完成"); } private static void SetProcuct3_10000() { List<Product> ProductList = new List<Product>(); for (int index = 40000; index < 60000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct3 執行完成"); } private static void SetProcuct4_10000() { List<Product> ProductList = new List<Product>(); for (int index = 60000; index < 80000; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); } Console.WriteLine("SetProcuct4 執行完成"); } } class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } }
我將每個方法中的資源隔離,性能顯而易見。
但是在操作500條數據時,顯然采用並行操作並不明智,並行所帶來的損耗比較大,在實際的開發中,還是要注意下是否有必要進行並行編程。
Parallel.For
將for循環替換成Parallel.For,並采用適合這個新方法的參數,就可以對這個已有的for循環進行重構,使其能夠充分利用並行化優勢。
需要注意的是:1.Parallel.For不支持浮點數的步進,使用的是Int32或Int64,每一次迭代的時候加1
2.由於循環體是並行運行,去迭代執行的順序無法保證
下面貼代碼

class Program { /* coder:釋迦苦僧*/ static void Main(string[] args) { Thread.Sleep(3000); ForSetProcuct_100(); Thread.Sleep(3000); ParallelForSetProcuct_100(); Console.ReadLine(); } private static void ForSetProcuct_100() { Stopwatch sw = new Stopwatch(); sw.Start(); List<Product> ProductList = new List<Product>(); for (int index = 1; index < 100; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); Console.WriteLine("for SetProcuct index: {0}", index); } sw.Stop(); Console.WriteLine("for SetProcuct 10 執行完成 耗時:{0}", sw.ElapsedMilliseconds); } private static void ParallelForSetProcuct_100() { Stopwatch sw = new Stopwatch(); sw.Start(); List<Product> ProductList = new List<Product>(); Parallel.For(1, 100, index => { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; ProductList.Add(model); Console.WriteLine("ForSetProcuct SetProcuct index: {0}", index); }); sw.Stop(); Console.WriteLine("ForSetProcuct SetProcuct 20000 執行完成 耗時:{0}", sw.ElapsedMilliseconds); } } class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } }
由圖中我們可以看出,使用Parallel.For所迭代的順序是無法保證的。
Parallel.ForEach
Parallel.ForEach提供一個並行處理一組數據的機制,可以利用一個范圍的整數作為一組數據,然后通過一個自定義的分區器將這個范圍轉換為一組數據塊,每一塊數據都通過循環的方式進行處理,而這些循環式並行執行的。
下面貼代碼:

class Program { /* coder:釋迦苦僧*/ static void Main(string[] args) { List<Product> ProductList =GetProcuctList(); Parallel.ForEach(ProductList, (model) => { Console.WriteLine(model.Name); }); Console.ReadLine(); } private static List<Product> GetProcuctList() { List<Product> result = new List<Product>(); for (int index = 1; index < 100; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; result.Add(model); } return result; } } class Product { public string Name { get; set; } public string Category { get; set; } public int SellPrice { get; set; } }
ParallelLoopState
ParallelLoopState該實例提供了以下兩個方法用於停止 Parallel.For,Parallel.ForEach
Break-這個方法告訴並行循環應該在執行了當前迭代后盡快地停止執行。吐過調用Break時正在處理迭代100,那么循環仍然會處理所有小於100的迭代。
Stop-這個方法告訴並行循環應該盡快停止執行,如果調用Stop時迭代100正在被處理,那么循環無法保證處理完所有小於100的迭代
下面貼代碼

class Program { /* coder:釋迦苦僧*/ static void Main(string[] args) { List<Product> productList = GetProcuctList_500(); Thread.Sleep(3000); Parallel.For(0, productList.Count, (i, loopState) => { if (i < 100) { Console.WriteLine("采用Stop index:{0}", i); } else { /* 滿足條件后 盡快停止執行,無法保證小於100的索引數據全部輸出*/ loopState.Stop(); return; } }); Thread.Sleep(3000); Parallel.For(0, productList.Count, (i, loopState) => { if (i < 100) { Console.WriteLine("采用Break index:{0}", i); } else { /* 滿足條件后 盡快停止執行,保證小於100的索引數據全部輸出*/ loopState.Break(); return; } }); Thread.Sleep(3000); Parallel.ForEach(productList, (model, loopState) => { if (model.SellPrice < 10) { Console.WriteLine("采用Stop index:{0}", model.SellPrice); } else { /* 滿足條件后 盡快停止執行,無法保證滿足條件的數據全部輸出*/ loopState.Stop(); return; } }); Thread.Sleep(3000); Parallel.ForEach(productList, (model, loopState) => { if (model.SellPrice < 10) { Console.WriteLine("采用Break index:{0}", model.SellPrice); } else { /* 滿足條件后 盡快停止執行,保證滿足條件的數據全部輸出*/ loopState.Break(); return; } }); Console.ReadLine(); } private static List<Product> GetProcuctList_500() { List<Product> result = new List<Product>(); for (int index = 1; index < 500; index++) { Product model = new Product(); model.Category = "Category" + index; model.Name = "Name" + index; model.SellPrice = index; result.Add(model); } return result; } }
由圖中可以看出Break可以保證輸出滿足所有條件的數據,而Stop則無法保證。
關於 Parallel 類提供的 Parallel.Invoke ,Parallel.For,Parallel.ForEach 的簡介入門到這,如有錯誤歡迎指正
作者:釋迦苦僧 出處:http://www.cnblogs.com/woxpp/p/3925094.html
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接。