· 最近一位高手同事實現了一個線程池,代碼品質超一流的說。特把代碼發上來,還有相關的線程池的文檔。 在物件導向編程中,建立和銷毀對象是很費時間的,因為建立一個對象要擷取記憶體資源或者其它更多資源。在Java中更是如此,虛擬機器將試圖跟蹤每一個對象,以便能夠在對象銷毀後進行記憶體回收。所以提高服務程式效率的一個手段就是儘可能減少建立和銷毀對象的次數,特別是一些很耗資源的對象建立和銷毀。如何利用已有對象來服務就是一個需要解決的關鍵問題,其實這就是一些“池化資源”技術產生的原因。比如大家所熟悉的資料庫連接池正是遵循這一思想而產生的,本文將介紹的線程池技術同樣符合這一思想。 目前,一些著名的大公司都特別看好這項技術,並早已經在他們的產品中應用該技術。比如IBM的WebSphere,IONA的Orbix 2000在SUN的 Jini中,Microsoft的MTS(Microsoft Transaction Server 2.0),COM+等。 多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。但如果對多線程應用不當,會增加對單個任務的處理時間。可以舉一個簡單的例子:
假設在一台伺服器完成一項任務的時間為TT1 建立線程的時間T2 線上程中執行任務的時間,包括線程間同步所需時間T3 線程銷毀的時間顯然T = T1+T2+T3。注意這是一個極度簡化的假設。可以看出T1,T3是多線程本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者並沒有注意到這一點,所以在程式中頻繁的建立或銷毀線程,這導致T1和T3在T中佔有相當比例。顯然這是突出了線程的弱點(T1,T3),而不是優點(並發性)。線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。它把T1,T3分別安排在伺服器程式的啟動和結束的時間段或者一些閒置時間段,這樣在伺服器程式處理客戶請求時,不會有T1,T3的開銷了。 下面是線程池的實現代碼:ThreadPool.h // ThreadPool.h: interface for the CThreadPool class.
//
//////////////////////////////////////////////////////////////////////#if !defined(AFX_THREADPOOL_H__E60DFA90_8F3E_11D4_8AF0_0000C03A07C8__INCLUDED_)
#define AFX_THREADPOOL_H__E60DFA90_8F3E_11D4_8AF0_0000C03A07C8__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <afxmt.h>
#include <afxtempl.h>
struct IJobDesc// 工作
{
public:
BOOL m_bProcessing;//工作正在處理中
IJobDesc()
{
m_bProcessing = FALSE;
}
};struct IWorker// 工人
{
virtual void ProcessJob(IJobDesc* pJob)=0;
};
class CThreadPool
{
friend static unsigned int __stdcall CThreadPool::ManagerProc(void* p);
friend static unsigned int __stdcall CThreadPool::WorkerProc(void* p);
protected:
enum ThreadPoolStatus { BUSY, IDLE, NORMAL };
public:
//interface to the outside
void Start(unsigned short nStatic, unsigned short nmax);
void Stop(bool bHash=false);
void AddJob(IJobDesc* pJob, IWorker* pWorker);
void RemoveJob(IJobDesc* pJob); //constructor and destructor
CThreadPool();
virtual ~CThreadPool();protected:
//interfaces public: UINT GetMgrWaitTime() const { return 1000*6; }private:
static unsigned int __stdcall ManagerProc(void* p);
static unsigned int __stdcall WorkerProc(void* p);
protected:
//manager thread
HANDLE m_hMgrThread;
HANDLE m_hMhtEvent; HANDLE m_hWorkerEvent;
protected:
//configuration parameters
mutable UINT m_nNumberOfStaticThreads;
mutable UINT m_nNumberOfTotalThreads;protected:
//helper functions
void AddThreads();
void RemoveThreads();
ThreadPoolStatus GetThreadPoolStatus();
void ChangeStatus(DWORD threadId, bool status);
void RemoveThread(DWORD threadId)
{
CSingleLock lock(&m_arrayCs);
lock.Lock();
m_threadMap.RemoveKey(threadId);
m_bRemovingThread = FALSE;
lock.Unlock(); }
BOOL GetNextProcessJob(IJobDesc ** pJob, IWorker** pWorker);
protected:
struct ThreadInfo
{
HANDLE m_hThread;
bool m_bBusyWorking;
ThreadInfo() { m_hThread=0; m_bBusyWorking=false; }
ThreadInfo(HANDLE handle, bool bBusy) { m_hThread=handle; m_bBusyWorking=bBusy; }
ThreadInfo(const ThreadInfo& info) { m_hThread=info.m_hThread; m_bBusyWorking=info.m_bBusyWorking; }
};
typedef struct JobInfo
{
IJobDesc * pJob;
IWorker * pWorker;
}JOBINFO;
CArray<JobInfo,JobInfo&>m_JobList;
CCriticalSection m_sJoblistSection;
int m_nCurProcessingJobIndex;
CCriticalSection m_IndexSection; //all the work threads
CMap<DWORD, DWORD&, ThreadInfo, ThreadInfo&> m_threadMap;
CCriticalSection m_arrayCs; BOOL m_bRemovingThread;
};#endif // !defined(AFX_THREADPOOL_H__E60DFA90_8F3E_11D4_8AF0_0000C03A07C8__INCLUDED_)文章引用自: · ThreadPool.cpp // ThreadPool.cpp: implementation of the CThreadPool class.
//
//////////////////////////////////////////////////////////////////////#include "stdafx.h"
#include "ThreadPool.h"
#include "process.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////CThreadPool::CThreadPool()
{
m_nCurProcessingJobIndex = -1;
m_bRemovingThread = FALSE;
m_JobList.RemoveAll();
}CThreadPool::~CThreadPool()
{
m_JobList.RemoveAll();
}void CThreadPool::Start(unsigned short nStatic, unsigned short nMax)
{
ASSERT(nMax>=nStatic);
m_nNumberOfStaticThreads=nStatic;
m_nNumberOfTotalThreads=nMax; m_hMhtEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
m_hWorkerEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//lock the resource
CSingleLock singleLock(&m_arrayCs);
singleLock.Lock(); // Attempt to lock the shared resource UINT dwThreadId;
m_hMgrThread = (HANDLE)_beginthreadex(NULL, 0, &ManagerProc, this, 0, (unsigned*)&dwThreadId); for(long n = 0; n < nStatic; n++)
{
DWORD threadId;
HANDLE handle = (HANDLE)_beginthreadex(NULL, 0, &WorkerProc, this, 0, (unsigned*)&threadId); TRACE1("generate a worker thread handle id is %d./n", threadId);
m_threadMap.SetAt(threadId, ThreadInfo(handle, false) );
}
singleLock.Unlock();
}void CThreadPool::Stop(bool bHash)
{
CSingleLock singleLock(&m_arrayCs);
singleLock.Lock(); // Attempt to lock the shared resource
SetEvent(m_hMhtEvent);
WaitForSingleObject(m_hMgrThread, INFINITE);
CloseHandle(m_hMhtEvent);
m_hMhtEvent = NULL;
//shut down all the worker threads
UINT nCount=m_threadMap.GetCount();
HANDLE* pThread = new HANDLE[nCount];
long n=0;
POSITION pos=m_threadMap.GetStartPosition();
DWORD threadId; ThreadInfo info;
while(pos!=NULL)
{
m_threadMap.GetNextAssoc(pos, threadId, info);
pThread[n++]=info.m_hThread;
} singleLock.Unlock();
SetEvent(m_hWorkerEvent);
DWORD rc=WaitForMultipleObjects(nCount, pThread, TRUE, 120000);//wait for 2 minutes, then start to kill threads
CloseHandle(m_hWorkerEvent);
m_hWorkerEvent = NULL; if(rc==WAIT_TIMEOUT&&bHash)
{
//some threads not terminated, we have to stop them.
DWORD exitCode;
for(int i=0; i<nCount; i++)
if (::GetExitCodeThread(pThread[i], &exitCode)==STILL_ACTIVE)
TerminateThread(pThread[i], 99);
}
delete[] pThread;
}unsigned int CThreadPool::ManagerProc(void* p)
{
//convert the parameter to the server pointer.
CThreadPool* pServer=(CThreadPool*)p; while(WAIT_OBJECT_0 != WaitForSingleObject(pServer->m_hMhtEvent, 6000))
{
//time out processing
TRACE0("Time out processing!/n");
//the manager will take a look at all the worker's status. The
if (pServer->GetThreadPoolStatus()==CThreadPool::BUSY)
pServer->AddThreads();
if (pServer->GetThreadPoolStatus()==CThreadPool::IDLE)
pServer->RemoveThreads();
} return 0;
}unsigned int CThreadPool::WorkerProc(void* p)
{
//convert the parameter to the server pointer.
CThreadPool* pServer=(CThreadPool*)p;
IWorker* pIWorker = NULL;
IJobDesc* pIJob= NULL; DWORD threadId=::GetCurrentThreadId();
// TRACE1("worker thread id is %d./n", threadId); while(true)
{
if(pServer->m_bRemovingThread)
{
pServer->RemoveThread(threadId);
break;
}
// TRACE(L"threadId %d:: begin GetTickCount %d /n",threadId,GetTickCount());
if (WAIT_OBJECT_0 == WaitForSingleObject(pServer->m_hWorkerEvent, 1))
{
break;
}
// TRACE(L"threadId %d:: end GetTickCount %d /n",threadId,GetTickCount());
if (pServer->GetNextProcessJob(&pIJob,&pIWorker))
{
// TRACE0("worker events comes in!/n");
//before processing, we need to change the status to busy.
pServer->ChangeStatus(threadId, true);
//retrieve the job description and agent pointer
if (pIJob && pIWorker)
{
pIJob->m_bProcessing = TRUE;
pIWorker->ProcessJob(pIJob);
pIJob->m_bProcessing = FALSE;
}
pServer->ChangeStatus(threadId, false);
}
} return 0;
}void CThreadPool::ChangeStatus(DWORD threadId, bool status)
{
CSingleLock singleLock(&m_arrayCs);
singleLock.Lock(); // Attempt to lock the shared resource //retrieve the current thread handle
ThreadInfo info;
m_threadMap.Lookup(threadId, info);
info.m_bBusyWorking=status;
m_threadMap.SetAt(threadId, info); singleLock.Unlock();
}void CThreadPool::AddJob(IJobDesc* pJob, IWorker* pWorker)
{
CThreadPool::JobInfo jobinfo;
jobinfo.pJob = pJob;
jobinfo.pWorker = pWorker;
CSingleLock lock(&m_sJoblistSection,TRUE);
m_JobList.Add(jobinfo);
}
void CThreadPool::RemoveJob(IJobDesc* pJob)
{
int size = m_JobList.GetSize();
JOBINFO jobinfo;
for (int i = 0; i < size ; i ++)
{
jobinfo = m_JobList.GetAt(i);
if (jobinfo.pJob == pJob)
{
CSingleLock lock(&m_sJoblistSection,TRUE);
m_JobList.RemoveAt(i);
return ;
}
}
}
BOOL CThreadPool::GetNextProcessJob(IJobDesc** pJob, IWorker** pWorker)
{
int size = m_JobList.GetSize();
if (size <= 0) return FALSE;
JOBINFO jobinfo;
//lock the resource
CSingleLock singleLock(&m_IndexSection);
singleLock.Lock(); //
m_nCurProcessingJobIndex ++;
if (m_nCurProcessingJobIndex >= size)
m_nCurProcessingJobIndex = 0;
jobinfo = m_JobList.GetAt(m_nCurProcessingJobIndex);
while (jobinfo.pJob && jobinfo.pJob->m_bProcessing && m_nCurProcessingJobIndex < size) {
m_nCurProcessingJobIndex ++;
if (m_nCurProcessingJobIndex < size)
jobinfo = m_JobList.GetAt(m_nCurProcessingJobIndex);
}
singleLock.Unlock(); if (m_nCurProcessingJobIndex >= size) return FALSE; *pJob = jobinfo.pJob;
*pWorker = jobinfo.pWorker;
return TRUE;
}
void CThreadPool::AddThreads()
{
int nCount=m_threadMap.GetCount();
int nTotal=min(nCount+2, m_nNumberOfTotalThreads);
for(int i=0; i<nTotal-nCount; i++)
{
if (i > 1) break; DWORD threadId;
HANDLE handle = (HANDLE)_beginthreadex(NULL, 0, &WorkerProc, this, 0, (unsigned*)&threadId);
TRACE1("generate a worker thread handle id is %d./n", threadId);
m_threadMap.SetAt(threadId, ThreadInfo(handle, false) );
}
}void CThreadPool::RemoveThreads()
{
if (m_bRemovingThread) return;
int nCount=m_threadMap.GetCount();
int nTotal=max(nCount-2, m_nNumberOfStaticThreads);
if (nCount > nTotal)
{
CSingleLock lock(&m_arrayCs,TRUE);
m_bRemovingThread = TRUE;
}
}CThreadPool::ThreadPoolStatus CThreadPool::GetThreadPoolStatus()
{
int nTotal = m_threadMap.GetCount();
POSITION pos=m_threadMap.GetStartPosition();
DWORD threadId; ThreadInfo info;
int nCount=0;
while(pos!=NULL)
{
m_threadMap.GetNextAssoc(pos, threadId, info);
if (info.m_bBusyWorking==true) nCount++;
}
if ( nCount/(1.0*nTotal) > 0.8 )
return BUSY;
if ( nCount/ (1.0*nTotal) < 0.2 )
return IDLE;
return NORMAL;
}文章引用自: http://blog.sina.com.cn/s/blog_4ab96a72010009sv.html