线程池的技术背景
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些“池化资源”技术产生的原因。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但如果对多线程应用不当,会增加对单个任务的处理时间。可以举一个简单的例子:
假设在一台服务器完成一项任务的时间为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产生的时间段,而且它还显著减少了创建线程的数目。
下面是一个具体的例子:
一 .h 文件
#pragma once
#include "stdafx.h"
#include <list>
#include <string>
using namespace std;
class CThreadPool;
//CSotreList
class CSotreList
{
public:
CSotreList();
virtual ~CSotreList();
public:
void Add(void* p);
void* Get();
bool Remove(void* p);
int GetSize();
private:
list<void*> m_list;
CRITICAL_SECTION m_sec;
};
//CJob
class CJob
{
public:
CJob(const char* name = "");
virtual ~CJob();
public:
string GetName() {return m_strName;}
virtual void Run();
private:
string m_strName;
};
//CThread
class CThread
{
public:
CThread();
virtual ~CThread();
public:
virtual void Run();
bool Start();
int GetThreadID();
bool Terminate();
bool SetPriority(int nPriority);
int GetPriority();
private:
static unsigned __stdcall ThreadProc(void* p);
protected:
HANDLE m_hThread;
unsigned m_nThreadID;
};
//CWorkerThread
class CWorkerThread : public CThread
{
public:
CWorkerThread();
virtual ~CWorkerThread();
public:
virtual void Run();
void Exit();
bool AssignJob(CJob* pJob);
void SetPool(CThreadPool* p);
private:
HANDLE m_hExitEvent;
HANDLE m_hJobEvent;
CJob* m_pJob;
CThreadPool* m_pPool;
CRITICAL_SECTION m_hSection;
};
//CThreadPool
class CThreadPool : public CThread
{
public:
CThreadPool();
CThreadPool(int nInitNum,int nGrow,bool blManage=false);
CThreadPool(int nInitNum,int nGrow,int nMax,int nIdleMax,int nIdleMin,bool blManage=true);
virtual ~CThreadPool();
public:
virtual void Run();
bool AddNewJob(CJob* pJob);
bool RemoveBusyThread(CThread* p);
bool Wait2Quit();
private:
void Exit();
bool Initialize();
bool UnInitialize();
private:
unsigned int m_nMax; //the maximum amount of threads in the pool
unsigned int m_nIdleMin; //The minimum amount of idle threads that should kept
unsigned int m_nIdleMax; //The maximum amount of idle threads that should kept
unsigned int m_nInit; //Initialize amount of threads in the pool
unsigned int m_nGrow; //The amount of threads that should be created when necessary
unsigned int m_nAmount; //The actual amount of threads in the pool now
HANDLE m_hExitEvent;
bool m_blManage; //Flag to add/remove idle threads automaticlly.
CSotreList m_listIdle;
CSotreList m_listBusy;
CSotreList m_listJob;
};
二 .cpp文件
#include "stdafx.h"
#include "ThreadPool.h"
#include <string>
#include <assert.h>
#include <algorithm>
using namespace std;
#define DEFAULT_THREADPOOL_MAX 60
#define DEFAULT_THREADPOOL_INIT 40
#define DEFAULT_THREADPOOL_GROW 0
#define DEFAULT_THREADPOOL_MAXIDLE 40
#define DEFAULT_THREADPOOL_MINIDLE 0
//CSotreList
CSotreList::CSotreList()
{
m_list.clear();
::InitializeCriticalSection(&m_sec);
}
CSotreList::~CSotreList()
{
::DeleteCriticalSection(&m_sec);
}
void CSotreList::Add(void* p)
{
::EnterCriticalSection(&m_sec);
m_list.push_back(p);
::LeaveCriticalSection(&m_sec);
}
void* CSotreList::Get()
{
void* p = NULL;
::EnterCriticalSection(&m_sec);
list<void*>::iterator iter = m_list.begin();
if(iter != m_list.end())
{
p = m_list.front();
m_list.erase(iter);
}
::LeaveCriticalSection(&m_sec);
return p;
}
bool CSotreList::Remove(void* p)
{
bool blRet = false;
::EnterCriticalSection(&m_sec);
list<void*>::iterator iter;
iter = find(m_list.begin(),m_list.end(),p);
if(iter != m_list.end())
{
m_list.erase(iter);
blRet = true;
}
::LeaveCriticalSection(&m_sec);
return blRet;
}
int CSotreList::GetSize()
{
int nRet = 0;
::EnterCriticalSection(&m_sec);
nRet = (int)m_list.size();
::LeaveCriticalSection(&m_sec);
return nRet;
}
//CJob
CJob::CJob(const char* name /*= ""*/)
{
m_strName = name;
}
CJob::~CJob()
{
}
void CJob::Run()
{
//Sleep(500);
}
//CThread
unsigned __stdcall CThread::ThreadProc(void* p)
{
CThread* pThread = (CThread*)p;
if(pThread == NULL)
{
_endthreadex(0);
return -1;
}
pThread->Run();
return 1;
}
CThread::CThread()
{
m_hThread = NULL;
m_nThreadID = 0;
}
CThread::~CThread()
{
}
bool CThread::Start()
{
Terminate();
try
{
m_hThread = (HANDLE)_beginthreadex(NULL,0,&(CThread::ThreadProc),this,0,&m_nThreadID);
}
catch(...)
{
m_hThread = NULL;
m_nThreadID = 0;
return false;
}
return true;
}
bool CThread::Terminate()
{
if(m_hThread != NULL)
{
::TerminateThread(m_hThread,1);
::CloseHandle(m_hThread);
m_hThread = NULL;
}
return true;
}
bool CThread::SetPriority(int nPriority)
{
if(m_hThread != NULL)
{
if(::SetThreadPriority(m_hThread,nPriority))
return true;
}
return false;
}
int CThread::GetPriority()
{
return ::GetThreadPriority(m_hThread);
}
int CThread::GetThreadID()
{
return m_nThreadID;
}
void CThread::Run()
{
}
//CWorkerThread
CWorkerThread::CWorkerThread() : CThread()
{
m_pJob = NULL;
m_pPool = NULL;
m_hExitEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
m_hJobEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
::InitializeCriticalSection(&m_hSection);
}
CWorkerThread::~CWorkerThread()
{
Exit();
if(m_hExitEvent != NULL)
{
CloseHandle(m_hExitEvent);
m_hExitEvent = NULL;
}
if(m_hJobEvent != NULL)
{
CloseHandle(m_hJobEvent);
m_hJobEvent = NULL;
}
::DeleteCriticalSection(&m_hSection);
}
void CWorkerThread::Exit()
{
if(m_hThread != NULL)
{
::SetEvent(m_hExitEvent);
::WaitForSingleObject(m_hThread,INFINITE);
::CloseHandle(m_hThread);
m_hThread = NULL;
}
}
bool CWorkerThread::AssignJob(CJob* pJob)
{
assert(pJob != NULL);
::EnterCriticalSection(&m_hSection);
m_pJob = pJob;
::LeaveCriticalSection(&m_hSection);
::SetEvent(m_hJobEvent); //Set event to do the job
return true;
}
void CWorkerThread::SetPool(CThreadPool* p)
{
m_pPool = p;
}
void CWorkerThread::Run()
{
HANDLE theHandles[2] = {m_hExitEvent,m_hJobEvent};
while(1)
{
DWORD dwRet = ::WaitForMultipleObjects(2,theHandles,FALSE,INFINITE);
if(dwRet == WAIT_OBJECT_0) //Exit
{
break;
}
else if(dwRet == WAIT_OBJECT_0+1) //Job
{
::EnterCriticalSection(&m_hSection);
m_pJob->Run();
delete m_pJob;
m_pJob = NULL;
::LeaveCriticalSection(&m_hSection);
if(m_pPool != NULL)
m_pPool->RemoveBusyThread(this);
}
else //impossible
{
break;
}
}
}
//CThreadPool
CThreadPool::CThreadPool() : CThread()
{
m_nMax = DEFAULT_THREADPOOL_MAX;
m_nIdleMin = DEFAULT_THREADPOOL_MINIDLE;
m_nIdleMax = DEFAULT_THREADPOOL_MAXIDLE;
m_nInit = DEFAULT_THREADPOOL_INIT;
m_nGrow = DEFAULT_THREADPOOL_GROW;
m_nAmount = m_nInit;
m_blManage = false;
m_hExitEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
Initialize();
Start(); //Start to assign jobs to threads
}
CThreadPool::CThreadPool(int nInitNum,int nGrow,bool blManage/*=false*/) : CThread()
{
assert(nInitNum>0 && nGrow>=0);
m_nInit = nInitNum;
m_nGrow = nGrow;
m_nAmount = m_nInit;
m_nMax = (nInitNum>=DEFAULT_THREADPOOL_MAX) ? nInitNum : DEFAULT_THREADPOOL_MAX;
m_nIdleMin = m_nAmount*2/10; //20%
m_nIdleMax = m_nAmount*8/10; //80%
m_blManage = blManage;
m_hExitEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
Initialize();
Start(); //Start to assign jobs to threads
}
CThreadPool::CThreadPool(int nInitNum,int nGrow,int nMax,int nIdleMax,int nIdleMin,bool blManage/*=true*/) : CThread()
{
assert(nInitNum>0 && nGrow>=0);
assert(nMax>=0 && nIdleMax>=0 && nIdleMin>=0);
assert(nIdleMin <= nIdleMax);
m_nInit = nInitNum;
m_nGrow = nGrow;
m_nAmount = m_nInit;
m_nMax = (nMax>=nInitNum) ? nMax : nInitNum;
m_nIdleMin = (nIdleMin<nInitNum) ? nIdleMin : m_nAmount*2/10; //20%
m_nIdleMax = (nIdleMax<nInitNum) ? m_nAmount*8/10 : nIdleMax; //80%
m_blManage = blManage;
m_hExitEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
Initialize();
Start(); //Start to assign jobs to threads
}
CThreadPool::~CThreadPool()
{
Exit();
UnInitialize();
if(m_hExitEvent != NULL)
{
CloseHandle(m_hExitEvent);
m_hExitEvent = NULL;
}
}
void CThreadPool::Exit()
{
if(m_hThread != NULL)
{
::SetEvent(m_hExitEvent);
::WaitForSingleObject(m_hThread,INFINITE);
::CloseHandle(m_hThread);
m_hThread = NULL;
}
}
bool CThreadPool::Initialize()
{
for(unsigned int i=0; i<m_nInit; i++)
{
CWorkerThread* pThread = new CWorkerThread();
pThread->SetPool(this);
pThread->Start();
m_listIdle.Add((void*)pThread);
}
return true;
}
bool CThreadPool::UnInitialize()
{
CJob* pJob = (CJob*)m_listJob.Get();
while(pJob != NULL)
{
delete pJob;
pJob = (CJob*)m_listJob.Get();
}
CWorkerThread* pThread = (CWorkerThread*)m_listBusy.Get();
while(pThread != NULL)
{
delete pThread;
pThread = (CWorkerThread*)m_listBusy.Get();
}
pThread = (CWorkerThread*)m_listIdle.Get();
while(pThread != NULL)
{
delete pThread;
pThread = (CWorkerThread*)m_listIdle.Get();
}
return true;
}
bool CThreadPool::Wait2Quit()
{
while(m_listJob.GetSize()>0 || m_listBusy.GetSize()>0)
Sleep(300);
return true;
}
bool CThreadPool::AddNewJob(CJob* pJob)
{
m_listJob.Add((void*)pJob);
return true;
}
bool CThreadPool::RemoveBusyThread(CThread* p)
{
if(m_listBusy.Remove(p))
{
m_listIdle.Add(p);
}
return true;
}
void CThreadPool::Run()
{
CWorkerThread* pThread = NULL;
CJob* pJob = NULL;
while(1)
{
if(::WaitForSingleObject(m_hExitEvent,0) == WAIT_OBJECT_0) //exit
{
if(pThread != NULL)
delete pThread;
break;
}
if(pThread == NULL)
{
//get idle thread
pThread = (CWorkerThread*)m_listIdle.Get();
if(pThread == NULL)
continue;
}
//get job
pJob = (CJob*)m_listJob.Get();
if(pJob == NULL)
{
Sleep(1000);
continue;
}
m_listBusy.Add(pThread);
pThread->AssignJob(pJob);
pThread = NULL;
}
}
三 应用
首先,从CJob继承一个新的Job类:
class CMyJob : public CJob
{
public:
CMyJob(const char* name = "");
virtual ~CMyJob();
public:
virtual void Run();
string m_strID;
string m_strInfo;
//others
};
CMyJob::CMyJob(const char* name /*= ""*/) : CJob(name)
{
m_strID = "";
m_strInfo= "";
//others
}
CMyJob::~CMyJob()
{
}
void CMyJob::Run()
{
//code
}
使用方法:
CThreadPool myPool(50,0); //池中有50个线程
for(int i=0; i<7000; i++)
{
char buf[10];
sprintf_s(buf,10,"%d",i);
CMyJob* pJob = new CMyJob(buf);
myPool.AddNewJob(pJob);
}
myPool.Wait2Quit();
说明:线程池的原理其实很简单,如果要写一个功能非常完整的线程池类,还要实现一些其它功能,如线程池的自动扩容和收缩,线程的挂起与恢复等等。在实际应用中,我觉得还是从实际出发吧,需要多少功能就先实现多少,以后有时间和精力的话,再完善。鉴于此,在本例中,引入了一些字段,但是并没有应用与实现相应的功能,这点还是不完善,有待改进。
参考: http://wenku.baidu.com/view/a4958362caaedd3383c4d316.html