簡介
在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