平行運算 (一):Parallel.For、Parallel.Foreach 用法及技巧

平行運算 (一):Parallel.For、Parallel.Foreach 用法及技巧

.NET Framework 4.0 增進了許多許多的功能,其中最令人興奮之一就是在平行運算(多執行緒)應用,將語法簡化的異常簡單,
對於平行運算的運作方式以及原理,可以參閱黃忠成老師的文章,可以有非常大的收穫,
這邊分享一下,實際運用Parallel.For、Parallel.Foreach的應用及一些技巧。

首先來我們來產生測試資料


//產生測試資料
List<string> testData = new List<string>();

//產生亂數字串
for (int i = 0; i < 100000; i++)
{
     testData.Add(Rand.RndChars(2, 10));
}

其中 Rand.RndChars 是自己寫的產生亂數字串Method

現在做一個實驗,我要取得測試資料中包含a 或 包含 abc的字串,
分別利用 Foreach 、 Parallel.For 、 Parallel.Foreach 方式來比較執行的時間

 

Foreach


//紀錄結果用
List<string> resultData = new List<string>();

//找出 testData 中包含a 或 包含 abc的字串
foreach (var item in testData)
{
     if (item.Contains("a") || item.Contains("abc"))
     {
            resultData.Add(item);       
     }
}

這段語法很簡單,取出 testData中 符合條件的資料,存入 resultData。

 

Parallel.For


            //紀錄結果用
            List<string> resultData = new List<string>();

            Parallel.For(0, testData.Count() - 1, (i, loopState) =>
            {
                string data = testData[i];
                if (data.Contains("a") || data.Contains("abc"))
                {
                    resultData.Add(data);                    
                }
            });

這邊說明一下 Parallel.For 的用法,
public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int> body); 
public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int, ParallelLoopState> body);
這是最常使用的重載,其中 fromInclusive 是起始元素索引、toExclusive 是結束元素索引,用法與 for 一樣,
其中 Action<int,ParalleLoopState> 表示要執行的delegate

重要!! 

在執行 Parallel.For 的範例時,一定會碰到一個很詭異的狀況,
有時候執行會正常,有時候會跳出以下的錯誤訊息

 2010-05-08_125040

2010-05-08_125453

這是因為 在 .Net 3.5 之前所提供的所有 Collections 都不是 thread-safe 的,必須使用.Net 4.0 , System.Collections.Concurrent Namespace 下的泛型

因此這邊我們改寫一下語法


        //紀錄結果用       
        ConcurrentStack<string> resultData2 = new ConcurrentStack<string>();

        Parallel.For(0, testData.Count() - 1, (i, loopState) =>
        {
            string data = testData[i];
            if (data.Contains("a") || data.Contains("abc"))
            {
                resultData2.Push(data);
             }
         });

改用 .Net 4.0 下提供的 ConcurrentStack<T>,來儲存結果 

 

Parallel.Foreach


        //紀錄結果用        
        ConcurrentStack<string> resultData2 = new ConcurrentStack<string>();

        Parallel.ForEach(testData, (item, loopState) =>
        {
            if (item.Contains("a") || item.Contains("abc"))
            {
                resultData2.Push(item);
            }
        });

public static ParallelLoopResult ForEach<TSource>( IEnumerable<TSource> source, Action<TSource, ParallelLoopState> body )
第一個參數 source 是使用資料來源,第二個參數 Action<TSource, ParallelLoopState> 是要執行的delegate 

 

測試結果

寫完以上語法後,
來比較一下效能吧

分別執行了20次,數字單位為 ms。

foreach:19Parallel.For:8Parallel.ForEach:8
foreach:21Parallel.For:7Parallel.ForEach:7
foreach:20Parallel.For:7Parallel.ForEach:7
foreach:19Parallel.For:7Parallel.ForEach:6
foreach:20Parallel.For:7Parallel.ForEach:8
foreach:19Parallel.For:5Parallel.ForEach:8
foreach:20Parallel.For:9Parallel.ForEach:6
foreach:19Parallel.For:8Parallel.ForEach:7
foreach:19Parallel.For:8Parallel.ForEach:8
foreach:19Parallel.For:8Parallel.ForEach:7
foreach:17Parallel.For:6Parallel.ForEach:7
foreach:17Parallel.For:7Parallel.ForEach:4
foreach:18Parallel.For:6Parallel.ForEach:5
foreach:17Parallel.For:4Parallel.ForEach:5
foreach:18Parallel.For:5Parallel.ForEach:7
foreach:20Parallel.For:9Parallel.ForEach:6
foreach:19Parallel.For:8Parallel.ForEach:6
foreach:19Parallel.For:9Parallel.ForEach:5
foreach:19Parallel.For:7Parallel.ForEach:5
foreach:21Parallel.For:7Parallel.ForEach:7

平均時間
foreach:18.17 ms
Parallel.For:7.17 ms
Parallel.Foreach:6.33 ms

語法非常的簡單,非常PowerFul。  

下一篇 再來比較 另一種寫法 PLINQ 的作法,以及使用Parallel.For、Parallel.Foreach的限制,以及如果真的非使用List<> 不想修改現有程式時,是否有其他方式來實作 

希望對大家有幫助~~

參考資料

How to: Write a Simple Parallel.For Loop

How to: Write a Simple Parallel.ForEach Loop

Introducing ConcurrentStack < T >

System.Collections.Concurrent Namespace

The Parallel Programming Of .NET Framework 4.0(1) – Beginning

The Parallel Programming Of .NET Framework 4.0(2) - Task Library

The Parallel Programming Of .NET Framework 4.0(3) - Deep Into Task Library

The Parallel Programming Of .NET Framework 4.0(4) - Inside Out Of Task Library

The Parallel Programming Of .NET Framework 4.0(5) - Dive to Parallel Programming




 


 

  • 如果您覺得這篇文章有幫助,請您幫忙推薦一下或按上方的""給予支持,非常感激
  • 歡迎轉載,但請註明出處
  • 文章內容多是自己找資料學習到的心得,如有不詳盡或錯誤的地方,請多多指教,謝謝