1. 程式人生 > >VC++ 執行緒池

VC++ 執行緒池

伺服器程式利用執行緒技術響應客戶請求已經司空見慣,可能您認為這樣做效率已經很高,但您有沒有想過優化一下使用執行緒的方法。該文章將向您介紹伺服器程式如何利用執行緒池來優化效能並提供一個簡單的執行緒池實現。
執行緒池的技術背景
在面向物件程式設計中,建立和銷燬物件是很費時間的,因為建立一個物件要獲取記憶體資源或者其它更多資源。在Java中更是如此,虛擬機器將試圖跟蹤每一個物件,以便能夠在物件銷燬後進行垃圾回收。所以提高服務程式效率的一個手段就是儘可能減少建立和銷燬物件的次數,特別是一些很耗資源的物件建立和銷燬。如何利用已有物件來服務就是一個需要解決的關鍵問題,其實這就是一些"池化資源"技術產生的原因。比如大家所熟悉的資料庫連線池正是遵循這一思想而產生的,本文將介紹的執行緒池技術同樣符合這一思想。

目前,一些著名的大公司都特別看好這項技術,並早已經在他們的產品中應用該技術。比如IBM的WebSphere,IONA的Orbix 2000在SUN的 Jini中,Microsoft的MTS(Microsoft Transaction Server 2.0),COM+等。

現在您是否也想在伺服器程式應用該項技術?

執行緒池技術如何提高伺服器程式的效能

我所提到伺服器程式是指能夠接受客戶請求並能處理請求的程式,而不只是指那些接受網路客戶請求的網路伺服器程式。

多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。但如果對多執行緒應用不當,會增加對單個任務的處理時間。可以舉一個簡單的例子:

假設在一臺伺服器完成一項任務的時間為T

T1 建立執行緒的時間
T2 線上程中執行任務的時間,包括執行緒間同步所需時間
T3 執行緒銷燬的時間

顯然T = T1+T2+T3。注意這是一個極度簡化的假設。

可以看出T1,T3是多執行緒本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些執行緒的使用者並沒有注意到這一點,所以在程式中頻繁的建立或銷燬執行緒,這導致T1和T3在T中佔有相當比例。顯然這是突出了執行緒的弱點(T1,T3),而不是優點(併發性)。

執行緒池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。它把T1,T3分別安排在伺服器程式的啟動和結束的時間段或者一些空閒的時間段,這樣在伺服器程式處理客戶請求時,不會有T1,T3的開銷了。

執行緒池不僅調整T1,T3產生的時間段,而且它還顯著減少了建立執行緒的數目。在看一個例子:

假設一個伺服器一天要處理50000個請求,並且每個請求需要一個單獨的執行緒完成。我們比較利用執行緒池技術和不利於執行緒池技術的伺服器處理這些請求時所產生的執行緒總數。線上程池中,執行緒數一般是固定的,所以產生執行緒總數不會超過執行緒池中執行緒的數目或者上限(以下簡稱執行緒池尺寸),而如果伺服器不利用執行緒池來處理這些請求則執行緒總數為50000。一般執行緒池尺寸是遠小於50000。所以利用執行緒池的伺服器程式不會為了建立50000而在處理請求時浪費時間,從而提高效率。

這些都是假設,不能充分說明問題,下面我將討論執行緒池的簡單實現並對該程式進行對比測試,以說明執行緒技術優點及應用領域。  現在來實現一下執行緒池 class ThreadPoolManage
{
private:  ThreadPoolManage();
 virtual~ThreadPoolManage(); private:  static ThreadPoolManage *m_ThreadPool;     
 static CArray<TemporaryThread *,TemporaryThread *> m_Temporarily; //臨時執行緒
 static CArray<ManageThread *,ManageThread *> m_ManageThread;      //最初執行緒
 static CCriticalSection section; public:  static ThreadPoolManage * GetThreadPoolManage();
 static void CloseThreadPoolManage(); protected:
    //執行緒回撥函式
 static DWORD WINAPI ExcuteMession(LPVOID n);
 static DWORD WINAPI ExcuteLeisureMession(LPVOID n); private:  //加入工作任務
 bool AddTask(Task *inTask); 
 bool IsAddTask();
    //起動與停止執行緒池
 bool StartThreadPool(int inCount);
 bool StopThreadPool();
 ///關閉一到多個空閒執行緒返回關閉的執行緒數
 int CloseTemporarilyThread(int count);
 //執行緒池中的總的執行緒數
 int GetThreadPoolCount();
};
#endif #include "Task.h"
class ManageThread
{
private:
 
 HANDLE mhThread;//執行緒控制代碼
 HANDLE mhEvent;//事件控制代碼(結束空閒執行緒通知)
 CSemaphore *m_sesction;//訊號量
 Task *pTask; public:  ManageThread();
 virtual~ManageThread(); public:
 
 //執行緒操作
 bool ExcuteThread();
 bool PostExitThread();
 bool TerminateExitThread();
 bool GetThreadIsLisure();//執行緒是否空閒
 bool SetLockThread(int tm=0);//鎖定當前執行緒(引數tm代表的是時間)  //字柄操作
 HANDLE GetThreadEvent();
 HANDLE GetThreadHandle();
 void  SetThreadHandle(HANDLE handle);     //設定當前執行緒工作的任務
 void SetThreadTask(Task *inTask); };
#endif class Task
{
public:
 Task();
 virtual ~Task();
public:
 virtual void ExcuteTask()=0; };
#endif 任務類說明: 這是我開發的一個UDP傳輸的類

class UdpTask :public Task

{

private:

       SOCKET mSckSender;

       HANDLE hRecThread;

       UdpClientInfo mInfor;

       bool   mIsSend;

       int    mSlindindWin;//可滑動視窗數

       list<WaitDefiniteData *> mWaitDefineList;

       double SuperTime;//超時時間(最小不得小到2)(=傳送前一個數據片的2)

       FILE * pFlie;  

       byte * _svd;

public:

       UdpTask();

       virtual ~UdpTask();

       void ExcuteTask();

       void SetClientInfo(CString inPath,RemoteEP inEP);

protected:

private:

       bool InitFile();

       void SendFileLoop();//傳送檔案

       void SendTcpConn();//TCP連線

       void SendSampleEnd();//傳送完畢

       BOOL HeavyspreadSample();//

重傳檢索

       void SendSpreadSample(WaitDefiniteData *inData);//重傳一個數據包

       void ConfirmSample(int inNum );//滑動視窗有關

       //傳送SOCKET打操作

       bool CreateSender();

       void DelecteSender();

       bool Send(const char * inBuffer,int inLength);

       //接受資料執行緒

       BOOL StartReceiving();

       void StopRecveiving();

       void RecveivingLoop();

       static DWORD WINAPI ReceivingThrd(void *pParam);

};