[C#]隨筆手扎 - Parallel & Lock

摘要:[C#]隨筆手扎 - Parallel & Lock

在.Net 4中新增入了Parallel用法,讓我們可以更快及更方便地進行平行化的處理;

做平行化有什麼好處?最主要的好處,莫過於更快地完成目標了;但是,Parallel下的結果,是否都是正確的呢?!

 

比方說,如果我要寫一個累計的功能,如果在未平行的前提下,一般都是用以下的方式達成:

        static void Main(string[] args)
        {
            int result = 0;
            for (int i = 0; i <= times; i++)
            {
                result += sum(i);
            }
            Console.WriteLine(result.ToString());
        }

        static int sum(int input)
        {
            int result = 0;
            for (int i = 1; i <= input; i++)
            {
                result += i;
            }
            return result;
        }

透過Parallel的話,可以這麼寫:

        static void Main(string[] args)
        {
            int result = 0;
            Parallel.For(1, times+1, i =>
            {
                result += sum(i);                
            });
            Console.WriteLine(result.ToString());
         }

         static int sum(int input)
        {
            int result = 0;
            for (int i = 1; i <= input; i++)
            {
                result += i;
            }
            return result;
          }

論理上,透過平行處理,只要您的電腦核心夠多及夠有力,取得結果的時間,會遠比未平行前來得少很多的!

 

不過,就如先前所說的,平行化的結果,似乎真的皆正確呢?

答案請看以下的測試結果(此結果中,一併展示了平行與未平行的差別):

 

範例程示碼如下:

        static void Main(string[] args)
        {
            Console.WriteLine("===No Paralle===");
            for (int i = 0; i < 10; i++)
            {
                NoParallel(26);
            }
            Console.WriteLine("===No Lock===");
            for (int i = 0; i < 10; i++)
            {
                ParallelNoLock(26);
            }
        }

        static void NoParallel(int times)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            int result = 0;
            for (int i = 0; i <= times; i++)
            {
                result += sum(i);
                Thread.Sleep(100);
            }
            sw.Stop();
            Console.WriteLine(result.ToString() + "(total cost " + (sw.ElapsedMilliseconds / 1000).ToString() + " seconds)");
        }

        static void ParallelNoLock(int times)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            int result = 0;
            Parallel.For(1, times+1, i =>
            {
                result += sum(i);
                Thread.Sleep(100);
            });
            sw.Stop();
            Console.WriteLine(result.ToString() + "(total cost " + (sw.ElapsedMilliseconds / 1000).ToString() + " seconds)");
        }

        static int sum(int input)
        {
            int result = 0;
            for (int i = 1; i <= input; i++)
            {
                result += i;
            }
            return result;
        }

以上的測試,我刻意在迴圈中加入延遲0.1秒,並且在雙核的筆電上進行測試,結果如下:

從結果中,很明顯地可以看到,同樣的功能,在重覆呼叫10次後,照理說,10次總計的結果應該要是一樣的,但是,平行化後,卻不是如此!

關於這問題,主要是因為平行化後的線程中,有發生問題,以致總計的結果有誤差!因此,關於這問題,事實上,解決方式很簡單,

只要在主要的區塊程序中,加上lock即可!lock的建議用法,如下圖示中,紅框所示處!

最後,透過加上lock的parallel,再做一次測試;此次將延遲加大至0.5秒,以便區分效果,並在8核的主機上進行測試如下:

從結果看來,最後總計的結果就是正確了,並且也能明顯地看出平行的效果了!

 

以上測試,僅供參考了,謝謝!