[C#.NET][Thread] 執行緒效能比較 - Parallel / Thread / ThreadPool

  • 41960
  • 0
  • 2013-08-16

[C#.NET][Thread] 執行緒效能比較 - Parallel / Thread / ThreadPool

在開始之前我們先來複習一下原本Thread類別以及TreadPool的用法,由主執行緒呼叫其他執行緒進行運算,運算結果會存放至一個全域變數,然後由主執行緒印出執行緒運算出來的結果。

 

宣告變數

public static int _ResultNumber = 0;
public static int _ActualNumber = 0;
static List<Thread> _Threads = null;
static object _lock = new object();
static int _LoopCount = 10000;
static AutoResetEvent[] _AutoEvents = new AutoResetEvent[_LoopCount];
static ManualResetEvent _ManualEvent = new ManualResetEvent(false);
static void Main(string[] args)
{
   //UseThreadClass();
   UseThreadPoolClass();
   // UseParallelClass();
}

 

PublicMethod只是用來驗証執行緒所演算的結果是否正確

static void PublicMethod()
{
    Console.WriteLine("Multi Thread calculate Result value = {0}", _ResultNumber);
    int result = 0;
    for (int i = 0; i < _LoopCount; i++)
        result += i;
    _ActualNumber = result;
    Console.WriteLine("Single Thread calculate Result value = {0}", _ActualNumber);
    Console.WriteLine("main thread exists..");
    Console.Read();
}

Thread類別用法:

使用若要主執行緒等待其他執行緒完成,可以使用Thread.Join方法,來確保所有執行緒已完成工作。

static void UseThreadClass()
{
    _ResultNumber = 0;
    _ActualNumber = 0;
    _Threads = new List<Thread>();
    for (int i = 0; i < _LoopCount; i++)
    {
        Thread t = new Thread(new ParameterizedThreadStart(ThreadDoWorker));
        _Threads.Add(t);
        t.Start(i);
    }
    foreach (Thread item in _Threads)
        item.Join();
    PublicMethod();
}

 

Doworker是執行緒的共用資源,為確保共用資源一次只有一條執行緒會進入,所以要把它用lock或是其他鎖定類別鎖起來,若沒鎖起來_ResultNumber計算出來的結果每次都會不一樣。

static void ThreadDoWorker(object i)
{
    lock (_lock)
    {
        Thread t = Thread.CurrentThread;
        _ResultNumber += (int)i;
        Console.WriteLine("No.{0}-Thread[{1}]:{2},Value is {3}", i, t.ManagedThreadId, t.ThreadState, _ResultNumber);
    }
}

Multi Thread計算結果與Single Thread計算結果一樣,表示執行緒的演算正確,只要沒有Join或是lock,主執行緒取得的結果就會不一樣,所以使用Thread要注意的事情比較多一點。

image

 

ThreadPool的用法:

ThreadPool因為沒有Join,所以必須要用WaitHandle來鎖定以及等待,在此範例我採用AutoResetEvent來處理,本來是想用WaitHandle.WaitAll方法,但是這個方法只能容許64個陣列索引,所以才改用AutoResetEvent來等待。有關WaitHandle.WaitAll方法等待用法可以參考http://msdn.microsoft.com/zh-tw/library/system.threading.manualresetevent.aspx

static void UseThreadPoolClass()
{
    _ResultNumber = 0;
    _ActualNumber = 0;
    ThreadPool.SetMaxThreads(10, 10);
    for (int i = 0; i < _LoopCount; i++)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(PoolDoWorker), i);
        _AutoEvents[i] = new AutoResetEvent(false);
    }
    for (int i = 0; i < _AutoEvents.Length; i++)
    {
        _AutoEvents[i].Set();
        Thread.Sleep(1);
    }
    //_ManualEvent.Set();
    //WaitHandle.WaitAll(_AutoEvents);
    PublicMethod();
}

 

用WaitOne方法來鎖定

static void PoolDoWorker(object i)
{
    //_ManualEvent.WaitOne();
    _AutoEvents[(int)i].WaitOne();
    Thread t = Thread.CurrentThread; 

    _ResultNumber += (int)i;
    Console.WriteLine("No.{0}-Thread[{1}]:{2},Value is {3}", i, t.ManagedThreadId, t.ThreadState, _ResultNumber);
    //_AutoEvents[(int)i].Set();
}

 

ThreadPool的比Thread的還要好,ThreadPool只用了Thread[13]~[16]條執行緒在處理工作

image

 

Parallel 的用法:

在.Net 4.0裡所新增的Parallel 類別不用鎖定不用等待,主執行緒就能取得正確的共用資源了,自動已經幫你做好處理同步機制了,真是佛心來的新功能~

static void UseParallelClass()
{
    _ResultNumber = 0;
    _ActualNumber = 0;
    Parallel.For(0, _LoopCount, ParallelDoWorker);
    PublicMethod();
}
 

執行緒共用資源裡也不用鎖定,它取代原本的Thread寫法, 看起就變的更精簡了。

static void ParallelDoWorker(int i)
{
    Thread t = Thread.CurrentThread;
    _ResultNumber += (int)i;
    Console.WriteLine("No.{0}-Thread[{1}]:{2},Value is {3}", i, t.ManagedThreadId, t.ThreadState, _ResultNumber);
    //Thread.Sleep(1);
}

PS.我在ParallelDoWorker方法裡用了Sleep方法,計算出來的結果會錯誤!

 

執行緒的效能也似乎比傳統呼叫Thread類別來的省資源,只用了Thread[9]~[13]處理工作。

image

 

接下來用單元測試來觀察哪一個用法的效能比較優,我在UseThreadPoolClass方法裡有用了Sleep(1),所以ThreadPool自然會輸給件Parallel,若是將測試數量減少Parallel仍略勝一籌

image

 

 

CPU使用情況

Thread類別

image

ThreadPool類別

image

Parallel類別

 

image

 

範例下載:Thread_Performance.zip

延伸閱讀:

http://www.dotblogs.com.tw/code6421/archive/2010/02/25/13766.aspx

http://www.dotblogs.com.tw/code6421/archive/2010/02/25/13767.aspx

http://www.dotblogs.com.tw/code6421/archive/2010/02/25/13768.aspx

http://www.dotblogs.com.tw/code6421/archive/2010/02/25/13769.aspx

http://www.dotblogs.com.tw/johnny/archive/2010/12/18/20225.aspx

http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/01/22/multicore-3.aspx

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo