C#實現多線程的方式:使用Parallel類


簡介

  在C#中實現多線程的另一個方式是使用Parallel類。 
  在.NET4中 ,另一個新增的抽象線程是Parallel類 。這個類定義了並行的for和foreach的 靜態方法。在為 for和 foreach定 義的語言中,循環從一個線程中運行 。Parallel類使用多個任務,因此使用多個線程來完成這個作業。 
  我們在前文中,對任務作出了一定的闡釋,有興趣的朋友可以前去查看。 
  Parallel.For()和 Parallel.ForEach()方法多次調用同一個方法,而 Parallel.Invoke()方法允許同時調用不同的方法。

使用Parallel.For()方法

  基本使用方法

  Parallel.For()方法類似於 C#的 for循環語旬,也是多次執行一個任務。使用Parallel.For()方法,可以並行運行迭代。 迭代的順序沒有定義。 
  在For()方法中,前兩個參數定義了循環的開頭和結束。示例從0迭代到 9。第 3個參數是一個Action<int>委托。 整數參數是循環的迭代次數,該 參數被傳遞給Action<int>委托引用的方法。Parallel.For()方法的返回類型是ParalleLoopResult結構,它提供了循環是否結束的信息。

1 ParallelLoopResult result = Parallel.For(0, 10, i =>
2             {
3                 Console.WriteLine("{0}, task : {1}, thread : {2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
4             });
5             Console.WriteLine(result.IsCompleted);
6             Console.ReadKey();

  在Parallel.For()的方法體中,把索引、任務標識符和線程標識符寫入控制台中。從下面的輸出截圖可以看出,由於每次循環都開啟了新的任務和線程,因此每個線程的執行順序是不能保證的。 
  這里寫圖片描述這里寫圖片描述

  中斷循環

  同For()循環類似,Parallel.For()方法也可以中斷循環的執行。 
  Parallel.For()方法的一個重載版本接受第3個Action<int, ParallelLoopState>類型的參數。使用這些參數定義一個方法,就可以調用ParalleLoopState的Break()或Stop()方法,以影響循環的結果。 
  注意,迭代的順序沒有定義。

 1  ParallelLoopResult result = Parallel.For(0, 10, (int i, ParallelLoopState pls) =>
 2             {
 3                 Console.WriteLine("i: {0}, task : {1}", i, Task.CurrentId);
 4                 Thread.Sleep(10);
 5                 if (i > 15)
 6                 {
 7                     pls.Break();
 8                 }
 9             });
10             Console.WriteLine(result.IsCompleted);
11             Console.WriteLine("Lowest break iteration: {0}", result.LowestBreakIteration);
12             Console.ReadKey();

下面是結果截圖: 
  這里寫圖片描述這里寫圖片描述 
   
  應用程序這次的運行說明,迭代在值大於15時中斷,但其他任務可以同時運行,有其他值的任務也可以運行。利用LowestBreakIteration屬性,可以忽略其他任務的結果。

  Parallel.For<TLocal>方法

  Parallel.For()方法可能使用幾個線程來執行循環 。如果需要對每個線程進行初始化,就可以使用Parallel.For<TLocal>方法。除了from和to對應的值之外,For()方法的泛型版本還接受3個委托參數。 
  第一個參數的類型是Func<TLocal> ,因為這里的例子對於TLocal使用字符串,所以該方法需要定義為Func<string>,即返回string的方法。這個方法僅對於用於執行迭代的每個線程調用一次。 
  第二個委托參數為循環體定義了委托。在示例中,該參數的類型是Func<int, ParallelLoopState, string, string>。 其中第一個參數是循環迭代,第二個參數 ParallelLoopstate允許停止循環,如前所述 。循環體方法通過第3個參數接收從init方法返回的值,循環體方法還需要返回一個值,其類型是用泛型for參數定義的。 
  For()方法的最后一個參數指定一個委托Action<TLocal>;在該示例中,接收一個字符串。 這個方法僅對於每個線程調用一次,這是一個線程退出方法。

 1 Parallel.For<string>(0, 20,
 2                 () =>
 3                 {
 4                     Console.WriteLine("init thread {0},\t task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
 5                     return string.Format("t{0}", Thread.CurrentThread.ManagedThreadId);
 6                 },
 7             (i, pls, str) =>
 8             {
 9                 Console.WriteLine("body i {0} \t str {1} \t thread {2} \t task {3}", i, str, Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
10                 Thread.Sleep(10);
11                 return string.Format("i \t{0}", i);
12             },
13             (str) =>
14             {
15                 Console.WriteLine("finally\t {0}", str);
16             });
17             Console.ReadKey();

程序運行結果: 
這里寫圖片描述這里寫圖片描述

  備注: 
  Parallel.For<TLocal> 方法 (Int32, Int32, Func<TLocal>, Func<Int32, ParallelLoopState, TLocal, TLocal>, Action<TLocal>
  類型參數 
  TLoca 
  線程本地數據的類型。

  參數

  fromInclusive 
  類型:System.Int32 
  開始索引(含)。

  toExclusive 
  類型:System.Int32 
  結束索引(不含)。

  localInit 
  類型:System.Func<TLocal> 
  用於返回每個任務的本地數據的初始狀態的函數委托。

  body 
  類型:System.Func<Int32, ParallelLoopState, TLocal, TLocal> 
  將為每個迭代調用一次的委托。

  localFinally 
  類型:System.Action<TLocal> 
  用於對每個任務的本地狀態執行一個最終操作的委托。

  返回值 
  類型:System.Threading.Tasks.ParallelLoopResult 
   
  在迭代范圍 (fromInclusive,toExclusive) ,為每個值調用一次body 委托。為它提供以下參數:迭代次數 (Int32)、可用來提前退出循環的ParallelLoopState實例以及可以在同一線程上執行的迭代之間共享的某些本地狀態。

  對於參與循環執行的每個任務調用 localInit 委托一次,並返回每個任務的初始本地狀態。 這些初始狀態傳遞給第一個在該任務上 調用的 body。 然后,每個后續正文調用返回可能修改過的狀態值,傳遞到下一個正文調用。 最后,每個任務上的最后正文調用返回傳遞給 localFinally 委托的狀態值。 每個任務調用 localFinally 委托一次,以對每個任務的本地狀態執行最終操作。此委托可以被多個任務同步調用;因此您必須同步對任何共享變量的訪問。

  Parallel.For方法比在它執行生存期的線程可能使用更多任務,作為現有的任務完成並被新任務替換。 這使基礎 TaskScheduler 對象有機會添加、更改或移除服務循環的線程。

  如果 fromInclusive 大於或等於 toExclusive,則該方法立即返回,而無需執行任何迭代。

使用Parallel.ForEach()方法

  基本使用方法

  Parallel.ForEach()方法遍歷實現了IEnumerable的集合,其方式類似於foreach語句,但以異步方式遍歷。這里也沒有確定遍歷順序。 

1 string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
2             ParallelLoopResult result = Parallel.ForEach<string>(data, (s) =>
3             {
4                 Console.WriteLine(s);
5             });
6             Console.ReadKey();

結果截圖: 
這里寫圖片描述這里寫圖片描述

  中斷循環

  如果需要中斷循環,就可以使用ForEach()方法的重載版本和ParallelLoopState參數。其方式與前面的For()方法相同。ForEach()方法的一個重載版本也可以用於訪問索引器,從而獲得迭代次數,如下所示: 

 1 string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
 2             ParallelLoopResult result = Parallel.ForEach<string>(data, (s, pls, l) =>
 3              {
 4                  Console.WriteLine("{0}\t{1}", s, l);
 5                  if (l > 10)
 6                  {
 7                      pls.Break();
 8                  }
 9              });
10             Console.WriteLine("Lowest break iteration: {0}", result.LowestBreakIteration);
11             Console.ReadKey();

結果截圖: 
這里寫圖片描述這里寫圖片描述

使用Parallel.Invoke()方法

  如果多個任務應並行運行,就可以使用Parallel.Invoke()方法。Parallel.Invoke()方法允許傳遞一個Action委托數組,在其中可以指定應運行的方法。 
  示例代碼傳遞了要並行調用的Foo()和Bar()方法: 

 1         static void Main(string[] args)
 2         {
 3             Parallel.Invoke(Foo, Bar);
 4             Console.ReadKey();
 5         }
 6         static void Foo()
 7         {
 8             Console.WriteLine("Foo");
 9         }
10 
11         static void Bar()
12         {
13             Console.WriteLine("Bar");
14         }

結果截圖: 
  這里寫圖片描述

轉載來源

http://blog.csdn.net/honantic/article/details/46876871


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM