LINUX c++线程池框架
本文给出了一个通用的线程池框架,该框架将与线程履行相干的任务进行了高层次的抽象,使之与具体的履行任务无关。别的该线程池具有动态伸缩性,它能按照履行任务的轻重主动调剂线程池中线程的数量。文章的最后,我们给出一个简单示例法度,经由过程该示例法度,我们会发明,经由过程该线程池框架履行多线程任务是多么的简单。
为什么须要线程池
今朝的大多半收集办事器,包含Web办事器、Email办事器以及数据库办事器等都具有一个共同点,就是单位时候内必须处理惩罚数量重大的连接恳求,但处理惩罚时候却相对较短。
传统多线程规划中我们采取的办事器模型则是一旦接管到恳求之后,即创建一个新的线程,由该线程履行任务。任务履行完毕后,线程退出,这就是是“即时创建,即时烧毁”的策略。尽管与创建过程比拟,创建线程的时候已经大大的缩短,然则若是提交给线程的任务是履行时候较短,并且履行次数极其频繁,那么办事器将处于不绝的创建线程,烧毁线程的状况。
我们将传统规划中的线程履行过程分为三个过程:T1、T2、T3。
T1:线程创建时候
T2:线程履行时候,包含线程的同步等时候
T3:线程烧毁时候
那么我们可以看出,线程本身的开销所占的比例为(T1+T3) / (T1+T2+T3)。若是线程履行的时候很短的话,这比开销可能占到20%-50%阁下。若是任务履行时候很频繁的话,这笔开销将是不成忽视的。
除此之外,线程池可以或许削减创建的线程个数。凡是线程池所容许的并发线程是有上界的,若是同时须要并发的线程数跨越上界,那么一项目组线程将会守候。而传统规划中,若是同时恳求数量为2000,那么最坏景象下,体系可能须要产生2000个线程。尽管这不是一个很大的数量,然则也有项目组机械可能达不到这种请求。是以线程池的呈现恰是着眼于削减线程池本身带来的开销。线程池采取预创建的技巧,在应用法度启动之后,将立即创建必然命量的线程(N1),放入余暇队列中。这些线程都是处于梗阻(Suspended)状况,不消费CPU,但占用较小的内存空间。当任务到来后,缓冲池选择一个余暇线程,把任务传入此线程中运行。当N1个线程都在处理惩罚任务后,缓冲池主动创建必然命量的新线程,用于处理惩罚更多的任务。在任务履行完毕后线程也不退出,而是持续对峙在池中守候下一次的任务。当体系斗劲余暇时,大项目组线程都一向处于暂停状况,线程池主动烧毁一项目组线程,收受接管体系资料。
基于这种预创建技巧,线程池将线程创建和烧毁本身所带来的开销分摊到了各个具体的任务上,履行次数越多,每个任务所分担到的线程本身开销则越小,不过我们别的可能须要推敲进去线程之间同步所带来的开销。
构建线程池框架
一般线程池都必须具备下面几个构成项目组:
线程池经管器:用于创建并经管线程池
工作线程: 线程池中实际履行的线程
任务接口: 尽管线程池大多半景象下是用来支撑收集办事器,然则我们将线程履行的任务抽象出来,形成任务接口,从而是的线程池与具体的任务无关。
任务队列:线程池的概念具体到实现则可能是队列,链表之类的数据布局,此中保存履行线程。
我们实现的通用线程池框架由五个首要项目组构成CThreadManage,CThreadPool,CThread,CJob,CWorkerThread,除此之外框架中还包含线程同步应用的类CThreadMutex和CCondition。
CJob是所有的任务的基类,其供给一个接口Run,所有的任务类都必须从该类持续,同时实现Run办法。该办法中实现具体的任务逻辑。
CThread是 Linux 中线程的包装,其封装了Linux线程最经常应用的属性和办法,它也是一个抽象类,是所有线程类的基类,具有一个接口Run。
CWorkerThread是实际被调剂和履行的线程类,其从CThread持续而来,实现了CThread中的Run办法。
CThreadPool是线程池类,其负责保存线程,开释线程以及调剂线程。
CThreadManage是线程池与用户的直接接口,其樊篱了内部的具体实现。
CThreadMutex用于线程之间的互斥。
CCondition则是前提变量的封装,用于线程之间的同步。
它们的类的持续关系如下图所示: (TO ADD)
线程池的时序很简单,如下图所示(TO ADD)。
CThreadManage直接跟客户端打交道,其接管须要创建的线程初始个数,并接管客户端提交的任务。这儿的任务是具体的非抽象的任务。CThreadManage的内部实际上调用的都是CThreadPool的相干操纵。CThreadPool创建具体的线程,并把客户端提交的任务分发给CWorkerThread,CWorkerThread实际履行具体的任务。
懂得体系组件
下面我们分隔来懂得体系中的各个组件。
CThreadManage
CThreadManage的功能很是简单,其供给最简单的办法,其类定义如下:
class CThreadManage
{
private:
CThreadPool* m_Pool;
int m_NumOfThread;
protected:
public:
CThreadManage();
CThreadManage(int num);
virtual ~CThreadManage();
void SetParallelNum(int num);
void Run(CJob* job,void* jobdata);
void TerminateAll(void);
};
此中m_Pool指向实际的线程池;m_NumOfThread是初始创建时辰容许创建的并发的线程个数。别的Run和TerminateAll办法也很是简单,只是简单的调用CThreadPool的一些相干办法罢了。其具体的实现如下:
CThreadManage::CThreadManage()
{
m_NumOfThread = 10;
m_Pool = new CThreadPool(m_NumOfThread);
}
CThreadManage::CThreadManage(int num)
{
m_NumOfThread = num;
m_Pool = new CThreadPool(m_NumOfThread);
}
CThreadManage::~CThreadManage()
{
if(NULL != m_Pool)
m_Pool;
}
void CThreadManage::SetParallelNum(int num)
{
m_NumOfThread = num;
}
void CThreadManage::Run(CJob* job,void* jobdata)
{
m_Pool->Run(job,jobdata);
}
void CThreadManage::TerminateAll(void)
{
m_Pool->TerminateAll();
}
CThread
CThread 类实现了对Linux中线程操纵的封装,它是所有线程的基类,也是一个抽象类,供给了一个抽象接口Run,所有的CThread都必须实现该Run办法。CThread的定义如下所示:
class CThread
{
private:
int m_ErrCode;
Semaphore m_ThreadSemaphore; //the inner semaphore, which is used to realize
unsigned long m_ThreadID;
bool m_Detach; //The thread is detached
bool m_CreateSuspended; //if suspend after creating
char* m_ThreadName;
ThreadState m_ThreadState; //the state of the thread
protected:
void SetErrcode(int errcode){m_ErrCode = errcode;}
static void* ThreadFunction(void*);
public:
CThread();
CThread(bool createsuspended,bool detach);
virtual ~CThread();
virtual void Run(void) = 0;
void SetThreadState(ThreadState state){m_ThreadState = state;}
bool Terminate(void); //Terminate the threa
bool Start(void); //Start to execute the thread
void Exit(void);
bool Wakeup(void);
ThreadState GetThreadState(void){return m_ThreadState;}
int GetLastError(void){return m_ErrCode;}
void SetThreadName(char* thrname){strcpy(m_ThreadName,thrname);}
char* GetThreadName(void){return m_ThreadName;}
int GetThreadID(void){return m_ThreadID;}
bool SetPriority(int priority);
int GetPriority(void);
int GetConcurrency(void);
void SetConcurrency(int num);
bool Detach(void);
bool Join(void);
bool Yield(void);
int Self(void);
};
线程的状况可以分为四种,余暇、繁忙、挂起、终止(包含正常退出和非正常退出)。因为今朝Linux线程库不支撑挂起操纵,是以,我们的此处的挂起操纵类似于暂停。若是线程创建后不想立即履行任务,那么我们可以将其“暂停”,若是须要运行,则唤醒。有一点必须重视的是,一旦线程开端履行任务,将不克不及被挂起,其将一向履行任务至完毕。
线程类的相干操纵均十分简单。线程的履行进口是从Start()函数开端,其将调用函数ThreadFunction,ThreadFunction再调用实际的Run函数,履行实际的任务。
CThreadPool
CThreadPool是线程的承载容器,一般可以将其实现为客栈、单向队列或者双向队列。在我们的体系中我们应用STL Vector对线程进行保存。CThreadPool的实现代码如下:
CThreadPool.h
#ifndef CTHREADPOOL_H_INCLUDED
#define CTHREADPOOL_H_INCLUDED
class CThreadPool
{
friend class CWorkerThread;
public:
CThreadPool();
CThreadPool(int initnum);
virtual ~CThreadPool();
void SetMaxNum(int maxnum){m_MaxNum = maxnum;}
int GetMaxNum(void){return m_MaxNum;}
void SetAvailLowNum(int minnum){m_AvailLow = minnum;}
int GetAvailLowNum(void){return m_AvailLow;}
void SetAvailHighNum(int highnum){m_AvailHigh = highnum;}
int GetAvailHighNum(void){return m_AvailHigh;}
int GetActualAvailNum(void){return m_AvailNum;}
int GetAllNum(void){return m_ThreadList.size();}
int GetBusyNum(void){return m_BusyList.size();}
void SetInitNum(int initnum){m_InitNum = initnum;}
int GetInitNum(void){return m_InitNum;}
void TerminateAll(void);
void Run(CJob* job,void* jobdata);
protected:
CWorkerThread* GetIdleThread(void);
void AppendToIdleList(CWorkerThread* jobthread);
void MoveToBusyList(CWorkerThread* idlethread);
void MoveToIdleList(CWorkerThread* busythread);
void DeleteIdleThread(int num);
void CreateIdleThread(int num);
public:
CThreadMutex m_BusyMutex; //when visit busy list,use m_BusyMutex to lock and unlock
CThreadMutex m_IdleMutex; //when visit idle list,use m_IdleMutex to lock and unlock
CThreadMutex m_JobMutex; //when visit job list,use m_JobMutex to lock and unlock
CThreadMutex m_VarMutex;
CCondition m_BusyCond; //m_BusyCond is used to sync busy thread list
CCondition m_IdleCond; //m_IdleCond is used to sync idle thread list
CCondition m_IdleJobCond; //m_JobCond is used to sync job list
CCondition m_MaxNumCond;
vector<CWorkerThread*> m_ThreadList;
vector<CWorkerThread*> m_BusyList; //Thread List
vector<CWorkerThread*> m_IdleList; //Idle List
private:
unsigned int m_InitNum; //Normal thread num;
unsigned int m_MaxNum; //the max thread num that can create at the same time
unsigned int m_AvailLow; //The min num of idle thread that shoule kept
unsigned int m_AvailHigh; //The max num of idle thread that kept at the same time
unsigned int m_AvailNum; //the normal thread num of idle num;
};
#endif // CTHREADPOOL_H_INCLUDED
CThreadPool.cpp
#include "CThreadPool.h"
CThreadPool::CThreadPool()
{
m_MaxNum = 50;
m_AvailLow = 5;
m_AvailHigh = 20;
m_InitNum=m_AvailNum = 10 ;
m_BusyList.clear();
m_IdleList.clear();
for(int i=0;i<m_InitNum;i++)
{
CWorkerThread* thr = new CWorkerThread();
thr->SetThreadPool(this);
AppendToIdleList(thr);
thr->Start();
}
}
CThreadPool::CThreadPool(int initnum)
{
assert(initnum>0 && initnum<=30);
m_MaxNum = 30;
m_AvailLow = initnum-10>0?initnum-10:3;
m_InitNum=m_AvailNum = initnum ;
m_AvailHigh = initnum+10;
m_BusyList.clear();
m_IdleList.clear();
for(int i=0;i<m_InitNum;i++)
{
CWorkerThread* thr = new CWorkerThread();
AppendToIdleList(thr);
thr->SetThreadPool(this);
thr->Start(); //begin the thread,the thread wait for job
}
}
CThreadPool::~CThreadPool()
{
TerminateAll();
}
void CThreadPool::TerminateAll()
{
for(int i=0;i < m_ThreadList.size();i++)
{
CWorkerThread* thr = m_ThreadList[i];
thr->Join();
}
return;
}
CWorkerThread* CThreadPool::GetIdleThread(void)
{
while(m_IdleList.size() ==0 )
{
m_IdleCond.Wait();
}
m_IdleMutex.Lock();
if(m_IdleList.size() > 0 )
{
CWorkerThread* thr = (CWorkerThread*)m_IdleList.front();
printf("Get Idle thread %dn",thr->GetThreadID());
m_IdleMutex.Unlock();
return thr;
}
m_IdleMutex.Unlock();
return NULL;
}
//add an idle thread to idle list
void CThreadPool::AppendToIdleList(CWorkerThread* jobthread)
{
m_IdleMutex.Lock();
m_IdleList.push_back(jobthread);
m_ThreadList.push_back(jobthread);
m_IdleMutex.Unlock();
}
//move and idle thread to busy thread
void CThreadPool::MoveToBusyList(CWorkerThread* idlethread)
{
m_BusyMutex.Lock();
m_BusyList.push_back(idlethread);
m_AvailNum--;
m_BusyMutex.Unlock();
m_IdleMutex.Lock();
vector<CWorkerThread*>::iterator pos;
pos = find(m_IdleList.begin(),m_IdleList.end(),idlethread);
if(pos !=m_IdleList.end())
{
m_IdleList.erase(pos);
}
m_IdleMutex.Unlock();
}
void CThreadPool::MoveToIdleList(CWorkerThread* busythread)
{
m_IdleMutex.Lock();
m_IdleList.push_back(busythread);
m_AvailNum++;
m_IdleMutex.Unlock();
m_BusyMutex.Lock();
vector<CWorkerThread*>::iterator pos;
pos = find(m_BusyList.begin(),m_BusyList.end(),busythread);
if(pos!=m_BusyList.end())
{
m_BusyList.erase(pos);
}
m_BusyMutex.Unlock();
m_IdleCond.Signal();
m_MaxNumCond.Signal();
}
//create num idle thread and put them to idlelist
void CThreadPool::CreateIdleThread(int num)
{
for(int i=0;i<num;i++)
{
CWorkerThread* thr = new CWorkerThread();
thr->SetThreadPool(this);
AppendToIdleList(thr);
m_VarMutex.Lock();
m_AvailNum++;
m_VarMutex.Unlock();
thr->Start(); //begin the thread,the thread wait for job
}
}
//create num idle thread and put them to idlelist
void CThreadPool::CreateIdleThread(int num)
{
for(int i=0;i<num;i++)
{
CWorkerThread* thr = new CWorkerThread();
thr->SetThreadPool(this);
AppendToIdleList(thr);
m_VarMutex.Lock();
m_AvailNum++;
m_VarMutex.Unlock();
thr->Start(); //begin the thread,the thread wait for job
}
}
void CThreadPool::DeleteIdleThread(int num)
{
printf("Enter into CThreadPool::DeleteIdleThreadn");
m_IdleMutex.Lock();
printf("Delete Num is %dn",num);
for(int i=0;i<num;i++)
{
CWorkerThread* thr;
if(m_IdleList.size() > 0 )
{
thr = (CWorkerThread*)m_IdleList.front();
printf("Get Idle thread %dn",thr->GetThreadID());
}
vector<CWorkerThread*>::iterator pos;
pos = find(m_IdleList.begin(),m_IdleList.end(),thr);
if(pos!=m_IdleList.end())
{
m_IdleList.erase(pos);
}
m_AvailNum--;
printf("The idle thread available num:%d n",m_AvailNum);
printf("The idlelist num:%d n",m_IdleList.size());
}
m_IdleMutex.Unlock();
}
void CThreadPool::Run(CJob* job,void* jobdata)
{
assert(job!=NULL);
//if the busy thread num adds to m_MaxNum,so we should wait
if(GetBusyNum() == m_MaxNum)
m_MaxNumCond.Wait();
if(m_IdleList.size()<m_AvailLow)
{
if(GetAllNum()+m_InitNum-m_IdleList.size() < m_MaxNum )
{
CreateIdleThread(m_InitNum-m_IdleList.size());
}
else
{
CreateIdleThread(m_MaxNum-GetAllNum());
}
}
CWorkerThread* idlethr = GetIdleThread();
if(idlethr !=NULL)
{
idlethr->m_WorkMutex.Lock();
MoveToBusyList(idlethr);
idlethr->SetThreadPool(this);
job->SetWorkThread(idlethr);
printf("Job is set to thread %d n",idlethr->GetThreadID());
idlethr->SetJob(job,jobdata);
}
}
在CThreadPool中存在两个链表,一个是余暇链表,一个是繁忙链表。Idle链表中存放所有的余暇过程,当线程履行任务时辰,其状况变为繁忙状况,同时从余暇链表中删除,并移至繁忙链表中。在CThreadPool的机关函数中,我们将履行下面的代码:
for(int i=0;i<m_InitNum;i++)
{
CWorkerThread* thr = new CWorkerThread();
AppendToIdleList(thr);
thr->SetThreadPool(this);
thr->Start(); //begin the thread,the thread wait for job
}
在该代码中,我们将创建m_InitNum个线程,创建之后即调用AppendToIdleList放入Idle链表中,因为今朝没有任务分发给这些线程,是以线程履行Start后将本身挂起。
事实上,线程池中容纳的线程数量并不是一成不变的,其会按照履行负载进行主动伸缩。为此在CThreadPool中设定四个变量:
m_InitNum:处世创建时线程池中的线程的个数。
m_MaxNum:当火线程池中所容许并发存在的线程的最大数量。
m_AvailLow:当火线程池中所容许存在的余暇线程的最小数量,若是余暇数量低于该值,注解负载可能过重,此时有须要增长余暇线程池的数量。实现中我们老是将线程调剂为m_InitNum个。
m_AvailHigh:当火线程池中所容许的余暇的线程的最大数量,若是余暇数量高于该值,注解当前负载可能较轻,此时将删除多余的余暇线程,删除后调剂数也为m_InitNum个。
m_AvailNum:今朝线程池中实际存在的线程的个数,其值介于m_AvailHigh和m_AvailLow之间。若是线程的个数始终保持在m_AvailLow和m_AvailHigh之间,则线程既不须要创建,也不须要删除,对峙均衡状况。是以如何设定m_AvailLow和m_AvailHigh的值,使得线程池最大可能的对峙均衡态,是线程池设计必须推敲的题目。
线程池在接管到新的任务之后,线程池起首要搜检是否有足够的余暇池可用。搜检分为三个步调:
(1)搜检当前处于繁忙状况的线程是否达到了设定的最大值m_MaxNum,若是达到了,注解今朝没有余暇线程可用,并且也不克不及创建新的线程,是以必须守候直到有线程履行完毕返回到余暇队列中。
(2)若是当前的余暇线程数量小于我们设定的最小的余暇数量m_AvailLow,则我们必须创建新的线程,默认景象下,创建后的线程数量应当为m_InitNum,是以创建的线程数量应当为( 当前余暇线程数与m_InitNum);然则有一种特别景象必须推敲,就是现有的线程总数加上创建后的线程数可能跨越m_MaxNum,是以我们必须对线程的创建差别对待。
if(GetAllNum()+m_InitNum-m_IdleList.size() < m_MaxNum )
CreateIdleThread(m_InitNum-m_IdleList.size());
else
CreateIdleThread(m_MaxNum-GetAllNum());
若是创建后总数不跨越m_MaxNum,则创建后的线程为m_InitNum;若是跨越了,则只创建( m_MaxNum-当火线程总数 )个。
(3)调用GetIdleThread办法查找余暇线程。若是当前没有余暇线程,则挂起;不然将任务指派给该线程,同时将其移入繁忙队列。
当线程履行完毕后,其会调用MoveToIdleList办法移入余暇链表中,此中还调用m_IdleCond.Signal()办法,唤醒GetIdleThread()中可能梗阻的线程。
CWorkerThread
CWorkerThread是CThread的派生类,是事实上的工作线程。在CThreadPool的机关函数中,我们创建了必然命量的CWorkerThread。一旦这些线程创建完毕,我们将调用Start()启动该线程。Start办法终极会调用Run办法。Run办法是个无穷轮回的过程。在没有接管到实际的任务的时辰,m_Job为NULL,此时线程将调用Wait办法进行守候,从而处于挂起状况。一旦线程池将具体的任务分发给该线程,其将被唤醒,从而通知线程从挂起的处所持续履行。CWorkerThread的完全定义如下:
class CWorkerThread:public CThread
{
private:
CThreadPool* m_ThreadPool;
CJob* m_Job;
void* m_JobData;
CThreadMutex m_VarMutex;
bool m_IsEnd;
protected:
public:
CCondition m_JobCond;
CThreadMutex m_WorkMutex;
CWorkerThread();
virtual ~CWorkerThread();
void Run();
void SetJob(CJob* job,void* jobdata);
CJob* GetJob(void){return m_Job;}
void SetThreadPool(CThreadPool* thrpool);
CThreadPool* GetThreadPool(void){return m_ThreadPool;}
};
CWorkerThread::CWorkerThread()
{
m_Job = NULL;
m_JobData = NULL;
m_ThreadPool = NULL;
m_IsEnd = false;
}
CWorkerThread::~CWorkerThread()
{
if(NULL != m_Job)
m_Job;
if(m_ThreadPool != NULL)
m_ThreadPool;
}
void CWorkerThread::Run()
{
SetThreadState(THREAD_RUNNING);
for(;;)
{
while(m_Job == NULL)
m_JobCond.Wait();
m_Job->Run(m_JobData);
m_Job->SetWorkThread(NULL);
m_Job = NULL;
m_ThreadPool->MoveToIdleList(this);
if(m_ThreadPool->m_IdleList.size() > m_ThreadPool->GetAvailHighNum())
{
m_ThreadPool->DeleteIdleThread(m_ThreadPool->m_IdleList.size()-m_T
hreadPool->GetInitNum());
}
m_WorkMutex.Unlock();
}
}
void CWorkerThread::SetJob(CJob* job,void* jobdata)
{
m_VarMutex.Lock();
m_Job = job;
m_JobData = jobdata;
job->SetWorkThread(this);
m_VarMutex.Unlock();
m_JobCond.Signal();
}
void CWorkerThread::SetThreadPool(CThreadPool* thrpool)
{
m_VarMutex.Lock();
m_ThreadPool = thrpool;
m_VarMutex.Unlock();
}
当线程履行任务之前起首必须断定余暇线程的数量是否低于m_AvailLow,若是低于,则必须创建足够的余暇线程,使其数量达到m_InitNum个,然后将调用MoveToBusyList()移出余暇队列,移入繁忙队列。当任务履行完毕后,其又调用MoveToIdleList()移出繁忙队列,移入余暇队列,守候新的任务。
除了Run办法之外,CWorkerThread中别的一个首要的办法就是SetJob,该办法将实际的任务赋值给线程。当没有任何履行任务即m_Job为NULL的时辰,线程将调用m_JobCond.Wait进行守候。一旦Job被赋值给线程,其将调用m_JobCond.Signal办法唤醒该线程。因为m_JobCond属于线程内部的变量,每个线程都保持一个m_JobCond,只有获得任务的线程才被唤醒,没有获得任务的将持续守候。无论一个线程何时被唤醒,其都将从守候的处所持续履行m_Job->Run(m_JobData),这是线程履行实际任务的处所。
在线程履行给定Job时代,我们必须防止别的一个Job又赋给该线程,是以在赋值之前,经由过程m_VarMutex进行锁定, Job履行时代,其于的Job将不克不及接洽关系到该线程;任务履行完毕,我们调用m_VarMutex.Unlock()进行解锁,此时,线程又可以接管新的履行任务。
在线程履行任务停止后返回余暇队列前,我们还须要断定当前余暇队列中的线程是否高于m_AvailHigh个。若是跨越m_AvailHigh,则必须从此中删除(m_ThreadPool->m_IdleList.size()-m_ThreadPool->GetInitNum())个线程,使线程数量对峙在m_InitNum个。
CJob
CJob类相对简单,其封装了任务的根蒂根基的属性和办法,此中最首要的是Run办法,代码如下:
class CJob
{
private:
int m_JobNo; //The num was assigned to the job
char* m_JobName; //The job name
CThread *m_pWorkThread; //The thread associated with the job
public:
CJob( void );
virtual ~CJob();
int GetJobNo(void) const { return m_JobNo; }
void SetJobNo(int jobno){ m_JobNo = jobno;}
char* GetJobName(void) const { return m_JobName; }
void SetJobName(char* jobname);
CThread *GetWorkThread(void){ return m_pWorkThread; }
void SetWorkThread ( CThread *pWorkThread ){
m_pWorkThread = pWorkThread;
}
virtual void Run ( void *ptr ) = 0;
};
CJob::CJob(void):m_pWorkThread(NULL),m_JobNo(0),m_JobName(NULL)
{
}
CJob::~CJob(){
if(NULL != m_JobName)
free(m_JobName);
}
void CJob::SetJobName(char* jobname)
{
if(NULL !=m_JobName) {
free(m_JobName);
m_JobName = NULL;
}
if(NULL !=jobname) {
m_JobName = (char*)malloc(strlen(jobname)+1);
strcpy(m_JobName,jobname);
}
}
线程池应用示例
至此我们给出了一个简单的与具体任务无关的线程池框架。应用该框架很是的简单,我们所须要的做的就是派生CJob类,将须要完成的任务实如今Run办法中。然后将该Job交由CThreadManage去履行。下面我们给出一个简单的示例法度
class CXJob:public CJob
{
public:
CXJob(){i=0;}
~CXJob(){}
void Run(void* jobdata) {
printf("The Job comes CXJOB\n");
sleep(2);
}
};
class CYJob:public CJob
{
public:
CYJob(){i=0;}
~CYJob(){}
void Run(void* jobdata) {
printf("The Job comes CYJob\n");
}
};
main()
{
CThreadManage* manage = new CThreadManage(10);
for(int i=0;i<40;i++)
{
CXJob* job = new CXJob();
manage->Run(job,NULL);
}
sleep(2);
CYJob* job = new CYJob();
manage->Run(job,NULL);
manage->TerminateAll();
}
CXJob和CYJob都是从Job类持续而来,其都实现了Run接口。CXJob只是简单的打印一句”The Job comes CXJob”,CYJob也只打印”The Job comes CYJob”,然后均休眠2秒钟。在主法度中我们初始创建10个工作线程。然后分别履行40次CXJob和一次CYJob。
线程池应用跋文
线程池合适场合
事实上,线程池并不是全能的。它有其特定的应用处合。线程池致力于削减线程本身的开销对应用所产生的影响,这是有前提的,前提就是线程本身开销与线程履行任务比拟不成忽视。若是线程本身的开销相对于线程任务履行开销而言是可以忽视不计的,那么此时线程池所带来的益处是不明显的,比如对于FTP办事器以及Telnet办事器,凡是传送文件的时候较长,开销较大,那么此时,我们采取线程池未必是幻想的办法,我们可以选择“即时创建,即时烧毁”的策略。
总之线程池凡是合适下面的几个场合:
(1) 单位时候内处理惩罚任务频繁并且任务处理惩罚时候短
(2) 对及时性请求较高。若是接管到任务后在创建线程,可能满足不了及时请求,是以必须采取线程池进行预创建。
(3) 必须经常面对高突发性事务,比如Web办事器,若是有足球转播,则办事器将产生重大的冲击。此时若是采取传统办法,则必须不绝的多量产生线程,烧毁线程。此时采取动态线程池可以避免这种景象的产生。
摘自 zhaoguoxian12345的专栏
本文给出了一个通用的线程池框架,该框架将与线程履行相干的任务进行了高层次的抽象,使之与具体的履行任务无关。别的该线程池具有动态伸缩性,它能按照履行任务的轻重主动调剂线程池中线程的数量。文章的最后,我们给出一个简单示例法度,经由过程该示例法度,我们会发明,经由过程该线程池框架履行多线程任务是多么的简单。
为什么须要线程池
今朝的大多半收集办事器,包含Web办事器、Email办事器以及数据库办事器等都具有一个共同点,就是单位时候内必须处理惩罚数量重大的连接恳求,但处理惩罚时候却相对较短。
传统多线程规划中我们采取的办事器模型则是一旦接管到恳求之后,即创建一个新的线程,由该线程履行任务。任务履行完毕后,线程退出,这就是是“即时创建,即时烧毁”的策略。尽管与创建过程比拟,创建线程的时候已经大大的缩短,然则若是提交给线程的任务是履行时候较短,并且履行次数极其频繁,那么办事器将处于不绝的创建线程,烧毁线程的状况。
我们将传统规划中的线程履行过程分为三个过程:T1、T2、T3。
T1:线程创建时候
T2:线程履行时候,包含线程的同步等时候
T3:线程烧毁时候
那么我们可以看出,线程本身的开销所占的比例为(T1+T3) / (T1+T2+T3)。若是线程履行的时候很短的话,这比开销可能占到20%-50%阁下。若是任务履行时候很频繁的话,这笔开销将是不成忽视的。
除此之外,线程池可以或许削减创建的线程个数。凡是线程池所容许的并发线程是有上界的,若是同时须要并发的线程数跨越上界,那么一项目组线程将会守候。而传统规划中,若是同时恳求数量为2000,那么最坏景象下,体系可能须要产生2000个线程。尽管这不是一个很大的数量,然则也有项目组机械可能达不到这种请求。是以线程池的呈现恰是着眼于削减线程池本身带来的开销。线程池采取预创建的技巧,在应用法度启动之后,将立即创建必然命量的线程(N1),放入余暇队列中。这些线程都是处于梗阻(Suspended)状况,不消费CPU,但占用较小的内存空间。当任务到来后,缓冲池选择一个余暇线程,把任务传入此线程中运行。当N1个线程都在处理惩罚任务后,缓冲池主动创建必然命量的新线程,用于处理惩罚更多的任务。在任务履行完毕后线程也不退出,而是持续对峙在池中守候下一次的任务。当体系斗劲余暇时,大项目组线程都一向处于暂停状况,线程池主动烧毁一项目组线程,收受接管体系资料。
基于这种预创建技巧,线程池将线程创建和烧毁本身所带来的开销分摊到了各个具体的任务上,履行次数越多,每个任务所分担到的线程本身开销则越小,不过我们别的可能须要推敲进去线程之间同步所带来的开销。
构建线程池框架
一般线程池都必须具备下面几个构成项目组:
线程池经管器:用于创建并经管线程池
工作线程: 线程池中实际履行的线程
任务接口: 尽管线程池大多半景象下是用来支撑收集办事器,然则我们将线程履行的任务抽象出来,形成任务接口,从而是的线程池与具体的任务无关。
任务队列:线程池的概念具体到实现则可能是队列,链表之类的数据布局,此中保存履行线程。
我们实现的通用线程池框架由五个首要项目组构成CThreadManage,CThreadPool,CThread,CJob,CWorkerThread,除此之外框架中还包含线程同步应用的类CThreadMutex和CCondition。
CJob是所有的任务的基类,其供给一个接口Run,所有的任务类都必须从该类持续,同时实现Run办法。该办法中实现具体的任务逻辑。
CThread是 Linux 中线程的包装,其封装了Linux线程最经常应用的属性和办法,它也是一个抽象类,是所有线程类的基类,具有一个接口Run。
CWorkerThread是实际被调剂和履行的线程类,其从CThread持续而来,实现了CThread中的Run办法。
CThreadPool是线程池类,其负责保存线程,开释线程以及调剂线程。
CThreadManage是线程池与用户的直接接口,其樊篱了内部的具体实现。
CThreadMutex用于线程之间的互斥。
CCondition则是前提变量的封装,用于线程之间的同步。
它们的类的持续关系如下图所示: (TO ADD)
线程池的时序很简单,如下图所示(TO ADD)。
CThreadManage直接跟客户端打交道,其接管须要创建的线程初始个数,并接管客户端提交的任务。这儿的任务是具体的非抽象的任务。CThreadManage的内部实际上调用的都是CThreadPool的相干操纵。CThreadPool创建具体的线程,并把客户端提交的任务分发给CWorkerThread,CWorkerThread实际履行具体的任务。
懂得体系组件
下面我们分隔来懂得体系中的各个组件。
CThreadManage
CThreadManage的功能很是简单,其供给最简单的办法,其类定义如下:
class CThreadManage
{
private:
CThreadPool* m_Pool;
int m_NumOfThread;
protected:
public:
CThreadManage();
CThreadManage(int num);
virtual ~CThreadManage();
void SetParallelNum(int num);
void Run(CJob* job,void* jobdata);
void TerminateAll(void);
};
此中m_Pool指向实际的线程池;m_NumOfThread是初始创建时辰容许创建的并发的线程个数。别的Run和TerminateAll办法也很是简单,只是简单的调用CThreadPool的一些相干办法罢了。其具体的实现如下:
CThreadManage::CThreadManage()
{
m_NumOfThread = 10;
m_Pool = new CThreadPool(m_NumOfThread);
}
CThreadManage::CThreadManage(int num)
{
m_NumOfThread = num;
m_Pool = new CThreadPool(m_NumOfThread);
}
CThreadManage::~CThreadManage()
{
if(NULL != m_Pool)
m_Pool;
}
void CThreadManage::SetParallelNum(int num)
{
m_NumOfThread = num;
}
void CThreadManage::Run(CJob* job,void* jobdata)
{
m_Pool->Run(job,jobdata);
}
void CThreadManage::TerminateAll(void)
{
m_Pool->TerminateAll();
}
CThread
CThread 类实现了对Linux中线程操纵的封装,它是所有线程的基类,也是一个抽象类,供给了一个抽象接口Run,所有的CThread都必须实现该Run办法。CThread的定义如下所示:
class CThread
{
private:
int m_ErrCode;
Semaphore m_ThreadSemaphore; //the inner semaphore, which is used to realize
unsigned long m_ThreadID;
bool m_Detach; //The thread is detached
bool m_CreateSuspended; //if suspend after creating
char* m_ThreadName;
ThreadState m_ThreadState; //the state of the thread
protected:
void SetErrcode(int errcode){m_ErrCode = errcode;}
static void* ThreadFunction(void*);
public:
CThread();
CThread(bool createsuspended,bool detach);
virtual ~CThread();
virtual void Run(void) = 0;
void SetThreadState(ThreadState state){m_ThreadState = state;}
bool Terminate(void); //Terminate the threa
bool Start(void); //Start to execute the thread
void Exit(void);
bool Wakeup(void);
ThreadState GetThreadState(void){return m_ThreadState;}
int GetLastError(void){return m_ErrCode;}
void SetThreadName(char* thrname){strcpy(m_ThreadName,thrname);}
char* GetThreadName(void){return m_ThreadName;}
int GetThreadID(void){return m_ThreadID;}
bool SetPriority(int priority);
int GetPriority(void);
int GetConcurrency(void);
void SetConcurrency(int num);
bool Detach(void);
bool Join(void);
bool Yield(void);
int Self(void);
};
线程的状况可以分为四种,余暇、繁忙、挂起、终止(包含正常退出和非正常退出)。因为今朝Linux线程库不支撑挂起操纵,是以,我们的此处的挂起操纵类似于暂停。若是线程创建后不想立即履行任务,那么我们可以将其“暂停”,若是须要运行,则唤醒。有一点必须重视的是,一旦线程开端履行任务,将不克不及被挂起,其将一向履行任务至完毕。
线程类的相干操纵均十分简单。线程的履行进口是从Start()函数开端,其将调用函数ThreadFunction,ThreadFunction再调用实际的Run函数,履行实际的任务。
CThreadPool
CThreadPool是线程的承载容器,一般可以将其实现为客栈、单向队列或者双向队列。在我们的体系中我们应用STL Vector对线程进行保存。CThreadPool的实现代码如下:
CThreadPool.h
#ifndef CTHREADPOOL_H_INCLUDED
#define CTHREADPOOL_H_INCLUDED
class CThreadPool
{
friend class CWorkerThread;
public:
CThreadPool();
CThreadPool(int initnum);
virtual ~CThreadPool();
void SetMaxNum(int maxnum){m_MaxNum = maxnum;}
int GetMaxNum(void){return m_MaxNum;}
void SetAvailLowNum(int minnum){m_AvailLow = minnum;}
int GetAvailLowNum(void){return m_AvailLow;}
void SetAvailHighNum(int highnum){m_AvailHigh = highnum;}
int GetAvailHighNum(void){return m_AvailHigh;}
int GetActualAvailNum(void){return m_AvailNum;}
int GetAllNum(void){return m_ThreadList.size();}
int GetBusyNum(void){return m_BusyList.size();}
void SetInitNum(int initnum){m_InitNum = initnum;}
int GetInitNum(void){return m_InitNum;}
void TerminateAll(void);
void Run(CJob* job,void* jobdata);
protected:
CWorkerThread* GetIdleThread(void);
void AppendToIdleList(CWorkerThread* jobthread);
void MoveToBusyList(CWorkerThread* idlethread);
void MoveToIdleList(CWorkerThread* busythread);
void DeleteIdleThread(int num);
void CreateIdleThread(int num);
public:
CThreadMutex m_BusyMutex; //when visit busy list,use m_BusyMutex to lock and unlock
CThreadMutex m_IdleMutex; //when visit idle list,use m_IdleMutex to lock and unlock
CThreadMutex m_JobMutex; //when visit job list,use m_JobMutex to lock and unlock
CThreadMutex m_VarMutex;
CCondition m_BusyCond; //m_BusyCond is used to sync busy thread list
CCondition m_IdleCond; //m_IdleCond is used to sync idle thread list
CCondition m_IdleJobCond; //m_JobCond is used to sync job list
CCondition m_MaxNumCond;
vector<CWorkerThread*> m_ThreadList;
vector<CWorkerThread*> m_BusyList; //Thread List
vector<CWorkerThread*> m_IdleList; //Idle List
private:
unsigned int m_InitNum; //Normal thread num;
unsigned int m_MaxNum; //the max thread num that can create at the same time
unsigned int m_AvailLow; //The min num of idle thread that shoule kept
unsigned int m_AvailHigh; //The max num of idle thread that kept at the same time
unsigned int m_AvailNum; //the normal thread num of idle num;
};
#endif // CTHREADPOOL_H_INCLUDED
CThreadPool.cpp
#include "CThreadPool.h"
CThreadPool::CThreadPool()
{
m_MaxNum = 50;
m_AvailLow = 5;
m_AvailHigh = 20;
m_InitNum=m_AvailNum = 10 ;
m_BusyList.clear();
m_IdleList.clear();
for(int i=0;i<m_InitNum;i++)
{
CWorkerThread* thr = new CWorkerThread();
thr->SetThreadPool(this);
AppendToIdleList(thr);
thr->Start();
}
}
CThreadPool::CThreadPool(int initnum)
{
assert(initnum>0 && initnum<=30);
m_MaxNum = 30;
m_AvailLow = initnum-10>0?initnum-10:3;
m_InitNum=m_AvailNum = initnum ;
m_AvailHigh = initnum+10;
m_BusyList.clear();
m_IdleList.clear();
for(int i=0;i<m_InitNum;i++)
{
CWorkerThread* thr = new CWorkerThread();
AppendToIdleList(thr);
thr->SetThreadPool(this);
thr->Start(); //begin the thread,the thread wait for job
}
}
CThreadPool::~CThreadPool()
{
TerminateAll();
}
void CThreadPool::TerminateAll()
{
for(int i=0;i < m_ThreadList.size();i++)
{
CWorkerThread* thr = m_ThreadList[i];
thr->Join();
}
return;
}
CWorkerThread* CThreadPool::GetIdleThread(void)
{
while(m_IdleList.size() ==0 )
{
m_IdleCond.Wait();
}
m_IdleMutex.Lock();
if(m_IdleList.size() > 0 )
{
CWorkerThread* thr = (CWorkerThread*)m_IdleList.front();
printf("Get Idle thread %dn",thr->GetThreadID());
m_IdleMutex.Unlock();
return thr;
}
m_IdleMutex.Unlock();
return NULL;
}
//add an idle thread to idle list
void CThreadPool::AppendToIdleList(CWorkerThread* jobthread)
{
m_IdleMutex.Lock();
m_IdleList.push_back(jobthread);
m_ThreadList.push_back(jobthread);
m_IdleMutex.Unlock();
}
//move and idle thread to busy thread
void CThreadPool::MoveToBusyList(CWorkerThread* idlethread)
{
m_BusyMutex.Lock();
m_BusyList.push_back(idlethread);
m_AvailNum--;
m_BusyMutex.Unlock();
m_IdleMutex.Lock();
vector<CWorkerThread*>::iterator pos;
pos = find(m_IdleList.begin(),m_IdleList.end(),idlethread);
if(pos !=m_IdleList.end())
{
m_IdleList.erase(pos);
}
m_IdleMutex.Unlock();
}
void CThreadPool::MoveToIdleList(CWorkerThread* busythread)
{
m_IdleMutex.Lock();
m_IdleList.push_back(busythread);
m_AvailNum++;
m_IdleMutex.Unlock();
m_BusyMutex.Lock();
vector<CWorkerThread*>::iterator pos;
pos = find(m_BusyList.begin(),m_BusyList.end(),busythread);
if(pos!=m_BusyList.end())
{
m_BusyList.erase(pos);
}
m_BusyMutex.Unlock();
m_IdleCond.Signal();
m_MaxNumCond.Signal();
}
//create num idle thread and put them to idlelist
void CThreadPool::CreateIdleThread(int num)
{
for(int i=0;i<num;i++)
{
CWorkerThread* thr = new CWorkerThread();
thr->SetThreadPool(this);
AppendToIdleList(thr);
m_VarMutex.Lock();
m_AvailNum++;
m_VarMutex.Unlock();
thr->Start(); //begin the thread,the thread wait for job
}
}
//create num idle thread and put them to idlelist
void CThreadPool::CreateIdleThread(int num)
{
for(int i=0;i<num;i++)
{
CWorkerThread* thr = new CWorkerThread();
thr->SetThreadPool(this);
AppendToIdleList(thr);
m_VarMutex.Lock();
m_AvailNum++;
m_VarMutex.Unlock();
thr->Start(); //begin the thread,the thread wait for job
}
}
void CThreadPool::DeleteIdleThread(int num)
{
printf("Enter into CThreadPool::DeleteIdleThreadn");
m_IdleMutex.Lock();
printf("Delete Num is %dn",num);
for(int i=0;i<num;i++)
{
CWorkerThread* thr;
if(m_IdleList.size() > 0 )
{
thr = (CWorkerThread*)m_IdleList.front();
printf("Get Idle thread %dn",thr->GetThreadID());
}
vector<CWorkerThread*>::iterator pos;
pos = find(m_IdleList.begin(),m_IdleList.end(),thr);
if(pos!=m_IdleList.end())
{
m_IdleList.erase(pos);
}
m_AvailNum--;
printf("The idle thread available num:%d n",m_AvailNum);
printf("The idlelist num:%d n",m_IdleList.size());
}
m_IdleMutex.Unlock();
}
void CThreadPool::Run(CJob* job,void* jobdata)
{
assert(job!=NULL);
//if the busy thread num adds to m_MaxNum,so we should wait
if(GetBusyNum() == m_MaxNum)
m_MaxNumCond.Wait();
if(m_IdleList.size()<m_AvailLow)
{
if(GetAllNum()+m_InitNum-m_IdleList.size() < m_MaxNum )
{
CreateIdleThread(m_InitNum-m_IdleList.size());
}
else
{
CreateIdleThread(m_MaxNum-GetAllNum());
}
}
CWorkerThread* idlethr = GetIdleThread();
if(idlethr !=NULL)
{
idlethr->m_WorkMutex.Lock();
MoveToBusyList(idlethr);
idlethr->SetThreadPool(this);
job->SetWorkThread(idlethr);
printf("Job is set to thread %d n",idlethr->GetThreadID());
idlethr->SetJob(job,jobdata);
}
}
在CThreadPool中存在两个链表,一个是余暇链表,一个是繁忙链表。Idle链表中存放所有的余暇过程,当线程履行任务时辰,其状况变为繁忙状况,同时从余暇链表中删除,并移至繁忙链表中。在CThreadPool的机关函数中,我们将履行下面的代码:
for(int i=0;i<m_InitNum;i++)
{
CWorkerThread* thr = new CWorkerThread();
AppendToIdleList(thr);
thr->SetThreadPool(this);
thr->Start(); //begin the thread,the thread wait for job
}
在该代码中,我们将创建m_InitNum个线程,创建之后即调用AppendToIdleList放入Idle链表中,因为今朝没有任务分发给这些线程,是以线程履行Start后将本身挂起。
事实上,线程池中容纳的线程数量并不是一成不变的,其会按照履行负载进行主动伸缩。为此在CThreadPool中设定四个变量:
m_InitNum:处世创建时线程池中的线程的个数。
m_MaxNum:当火线程池中所容许并发存在的线程的最大数量。
m_AvailLow:当火线程池中所容许存在的余暇线程的最小数量,若是余暇数量低于该值,注解负载可能过重,此时有须要增长余暇线程池的数量。实现中我们老是将线程调剂为m_InitNum个。
m_AvailHigh:当火线程池中所容许的余暇的线程的最大数量,若是余暇数量高于该值,注解当前负载可能较轻,此时将删除多余的余暇线程,删除后调剂数也为m_InitNum个。
m_AvailNum:今朝线程池中实际存在的线程的个数,其值介于m_AvailHigh和m_AvailLow之间。若是线程的个数始终保持在m_AvailLow和m_AvailHigh之间,则线程既不须要创建,也不须要删除,对峙均衡状况。是以如何设定m_AvailLow和m_AvailHigh的值,使得线程池最大可能的对峙均衡态,是线程池设计必须推敲的题目。
线程池在接管到新的任务之后,线程池起首要搜检是否有足够的余暇池可用。搜检分为三个步调:
(1)搜检当前处于繁忙状况的线程是否达到了设定的最大值m_MaxNum,若是达到了,注解今朝没有余暇线程可用,并且也不克不及创建新的线程,是以必须守候直到有线程履行完毕返回到余暇队列中。
(2)若是当前的余暇线程数量小于我们设定的最小的余暇数量m_AvailLow,则我们必须创建新的线程,默认景象下,创建后的线程数量应当为m_InitNum,是以创建的线程数量应当为( 当前余暇线程数与m_InitNum);然则有一种特别景象必须推敲,就是现有的线程总数加上创建后的线程数可能跨越m_MaxNum,是以我们必须对线程的创建差别对待。
if(GetAllNum()+m_InitNum-m_IdleList.size() < m_MaxNum )
CreateIdleThread(m_InitNum-m_IdleList.size());
else
CreateIdleThread(m_MaxNum-GetAllNum());
若是创建后总数不跨越m_MaxNum,则创建后的线程为m_InitNum;若是跨越了,则只创建( m_MaxNum-当火线程总数 )个。
(3)调用GetIdleThread办法查找余暇线程。若是当前没有余暇线程,则挂起;不然将任务指派给该线程,同时将其移入繁忙队列。
当线程履行完毕后,其会调用MoveToIdleList办法移入余暇链表中,此中还调用m_IdleCond.Signal()办法,唤醒GetIdleThread()中可能梗阻的线程。
CWorkerThread
CWorkerThread是CThread的派生类,是事实上的工作线程。在CThreadPool的机关函数中,我们创建了必然命量的CWorkerThread。一旦这些线程创建完毕,我们将调用Start()启动该线程。Start办法终极会调用Run办法。Run办法是个无穷轮回的过程。在没有接管到实际的任务的时辰,m_Job为NULL,此时线程将调用Wait办法进行守候,从而处于挂起状况。一旦线程池将具体的任务分发给该线程,其将被唤醒,从而通知线程从挂起的处所持续履行。CWorkerThread的完全定义如下:
class CWorkerThread:public CThread
{
private:
CThreadPool* m_ThreadPool;
CJob* m_Job;
void* m_JobData;
CThreadMutex m_VarMutex;
bool m_IsEnd;
protected:
public:
CCondition m_JobCond;
CThreadMutex m_WorkMutex;
CWorkerThread();
virtual ~CWorkerThread();
void Run();
void SetJob(CJob* job,void* jobdata);
CJob* GetJob(void){return m_Job;}
void SetThreadPool(CThreadPool* thrpool);
CThreadPool* GetThreadPool(void){return m_ThreadPool;}
};
CWorkerThread::CWorkerThread()
{
m_Job = NULL;
m_JobData = NULL;
m_ThreadPool = NULL;
m_IsEnd = false;
}
CWorkerThread::~CWorkerThread()
{
if(NULL != m_Job)
m_Job;
if(m_ThreadPool != NULL)
m_ThreadPool;
}
void CWorkerThread::Run()
{
SetThreadState(THREAD_RUNNING);
for(;;)
{
while(m_Job == NULL)
m_JobCond.Wait();
m_Job->Run(m_JobData);
m_Job->SetWorkThread(NULL);
m_Job = NULL;
m_ThreadPool->MoveToIdleList(this);
if(m_ThreadPool->m_IdleList.size() > m_ThreadPool->GetAvailHighNum())
{
m_ThreadPool->DeleteIdleThread(m_ThreadPool->m_IdleList.size()-m_T
hreadPool->GetInitNum());
}
m_WorkMutex.Unlock();
}
}
void CWorkerThread::SetJob(CJob* job,void* jobdata)
{
m_VarMutex.Lock();
m_Job = job;
m_JobData = jobdata;
job->SetWorkThread(this);
m_VarMutex.Unlock();
m_JobCond.Signal();
}
void CWorkerThread::SetThreadPool(CThreadPool* thrpool)
{
m_VarMutex.Lock();
m_ThreadPool = thrpool;
m_VarMutex.Unlock();
}
当线程履行任务之前起首必须断定余暇线程的数量是否低于m_AvailLow,若是低于,则必须创建足够的余暇线程,使其数量达到m_InitNum个,然后将调用MoveToBusyList()移出余暇队列,移入繁忙队列。当任务履行完毕后,其又调用MoveToIdleList()移出繁忙队列,移入余暇队列,守候新的任务。
除了Run办法之外,CWorkerThread中别的一个首要的办法就是SetJob,该办法将实际的任务赋值给线程。当没有任何履行任务即m_Job为NULL的时辰,线程将调用m_JobCond.Wait进行守候。一旦Job被赋值给线程,其将调用m_JobCond.Signal办法唤醒该线程。因为m_JobCond属于线程内部的变量,每个线程都保持一个m_JobCond,只有获得任务的线程才被唤醒,没有获得任务的将持续守候。无论一个线程何时被唤醒,其都将从守候的处所持续履行m_Job->Run(m_JobData),这是线程履行实际任务的处所。
在线程履行给定Job时代,我们必须防止别的一个Job又赋给该线程,是以在赋值之前,经由过程m_VarMutex进行锁定, Job履行时代,其于的Job将不克不及接洽关系到该线程;任务履行完毕,我们调用m_VarMutex.Unlock()进行解锁,此时,线程又可以接管新的履行任务。
在线程履行任务停止后返回余暇队列前,我们还须要断定当前余暇队列中的线程是否高于m_AvailHigh个。若是跨越m_AvailHigh,则必须从此中删除(m_ThreadPool->m_IdleList.size()-m_ThreadPool->GetInitNum())个线程,使线程数量对峙在m_InitNum个。
CJob
CJob类相对简单,其封装了任务的根蒂根基的属性和办法,此中最首要的是Run办法,代码如下:
class CJob
{
private:
int m_JobNo; //The num was assigned to the job
char* m_JobName; //The job name
CThread *m_pWorkThread; //The thread associated with the job
public:
CJob( void );
virtual ~CJob();
int GetJobNo(void) const { return m_JobNo; }
void SetJobNo(int jobno){ m_JobNo = jobno;}
char* GetJobName(void) const { return m_JobName; }
void SetJobName(char* jobname);
CThread *GetWorkThread(void){ return m_pWorkThread; }
void SetWorkThread ( CThread *pWorkThread ){
m_pWorkThread = pWorkThread;
}
virtual void Run ( void *ptr ) = 0;
};
CJob::CJob(void):m_pWorkThread(NULL),m_JobNo(0),m_JobName(NULL)
{
}
CJob::~CJob(){
if(NULL != m_JobName)
free(m_JobName);
}
void CJob::SetJobName(char* jobname)
{
if(NULL !=m_JobName) {
free(m_JobName);
m_JobName = NULL;
}
if(NULL !=jobname) {
m_JobName = (char*)malloc(strlen(jobname)+1);
strcpy(m_JobName,jobname);
}
}
线程池应用示例
至此我们给出了一个简单的与具体任务无关的线程池框架。应用该框架很是的简单,我们所须要的做的就是派生CJob类,将须要完成的任务实如今Run办法中。然后将该Job交由CThreadManage去履行。下面我们给出一个简单的示例法度
class CXJob:public CJob
{
public:
CXJob(){i=0;}
~CXJob(){}
void Run(void* jobdata) {
printf("The Job comes CXJOB\n");
sleep(2);
}
};
class CYJob:public CJob
{
public:
CYJob(){i=0;}
~CYJob(){}
void Run(void* jobdata) {
printf("The Job comes CYJob\n");
}
};
main()
{
CThreadManage* manage = new CThreadManage(10);
for(int i=0;i<40;i++)
{
CXJob* job = new CXJob();
manage->Run(job,NULL);
}
sleep(2);
CYJob* job = new CYJob();
manage->Run(job,NULL);
manage->TerminateAll();
}
CXJob和CYJob都是从Job类持续而来,其都实现了Run接口。CXJob只是简单的打印一句”The Job comes CXJob”,CYJob也只打印”The Job comes CYJob”,然后均休眠2秒钟。在主法度中我们初始创建10个工作线程。然后分别履行40次CXJob和一次CYJob。
线程池应用跋文
线程池合适场合
事实上,线程池并不是全能的。它有其特定的应用处合。线程池致力于削减线程本身的开销对应用所产生的影响,这是有前提的,前提就是线程本身开销与线程履行任务比拟不成忽视。若是线程本身的开销相对于线程任务履行开销而言是可以忽视不计的,那么此时线程池所带来的益处是不明显的,比如对于FTP办事器以及Telnet办事器,凡是传送文件的时候较长,开销较大,那么此时,我们采取线程池未必是幻想的办法,我们可以选择“即时创建,即时烧毁”的策略。
总之线程池凡是合适下面的几个场合:
(1) 单位时候内处理惩罚任务频繁并且任务处理惩罚时候短
(2) 对及时性请求较高。若是接管到任务后在创建线程,可能满足不了及时请求,是以必须采取线程池进行预创建。
(3) 必须经常面对高突发性事务,比如Web办事器,若是有足球转播,则办事器将产生重大的冲击。此时若是采取传统办法,则必须不绝的多量产生线程,烧毁线程。此时采取动态线程池可以避免这种景象的产生。
摘自 zhaoguoxian12345的专栏