首页 今日头条正文

搞笑一家人,Linux C++线程池结构详解(值得保藏),app

1. 为什么需求线程池

  现在的大多数网络服务器,包含Web服务器、Email服务器以及数据库服务器等都具有一个共同点,便是单位时刻内有必要处理数目巨大的衔接恳求,但处理时刻却相对较短。

  传统多线程计划中咱们选用的服务器模型则是一旦承受到恳求之后,即创立一个新的线程,由该线程履行使命。使命履行结束后,线程退出,这便是是“即时创立,即时毁掉”的战略。虽然与创立进程比较,创立线程的时刻现已大大的缩短,可是假如提交给线程的使命是履行时刻较短,并且履行次数极端频频,那么服务器将处于不断的创立线程,毁掉线程的状况。

  咱们将传统计划中的线程履行进程分为三个进程:T1、T2、T3。

 搞笑一家人,Linux C++线程池结构详解(值得保藏),app T1:线程创立时刻

  T2:线程履行时刻,包含线程的同步等时刻

  T3:线程毁掉时刻

  那么咱们能够看出,线程自身的开支所占的份额为(T1+T3) 童理民/ (T1+T2+T3)。假如线程履行的时刻很短的话,这比开支或许占到20%-50%左右。假如使命履行时刻很频频的话,这笔开支将是不行疏忽的。

  除此之外,线程池能够削减创立的线程个数。一般线程池所答应的并发线程是有上界的,假如一起需求并发的线程数超越上界,那么一部分线程将会等候。而传统计划中,假如一起恳求数目为2000,那么最坏状况下,体系或许需求发作2000个线程。虽然这不是一个很大的数目,可是也有部分机器或许达不到这种要求。

  因而线程池的呈现正是着眼于削减线程池自身带来的开支。线程池选用预创立的技能,在使用程序发动之后,将当即创立必定数量的线程(N1),放入闲暇行列中。这些线程都是处于堵塞(Suspended)状况,不耗费CPU,但占用较小的内存空间。当使命到来后,缓冲池挑选一个闲暇线程,把使命传入此线程中运转。当N1个线程都在处理使命后,缓冲池主动创立必定数量的新线程,用于处理更多的使命。在使命履行结束后线程也不退出,而是持续坚持在池中等候下一次的使命。当体系比较闲暇时,大部分线程都一向处于暂停状况,线程池主动毁掉一部分线程,收回体系资源。

  依据这种预创立技能,线程池将线程创立和毁掉自身所带来的开支分摊到了各个详细的使命上,履行次数越多,每个使命所分管到的线程自身开支则越小,不过咱们别的或许需求考虑进去线程之间同步所带来的开支。

2. 构建线程池结构

  一般线程池都有必要具有下面几个组成部分:

  线程池办理器:用于创立并办理线程池

  作业线程:线程池中实践履行的线程

  使命接口:虽然线程池大多数状况下是用来支撑网络服务器,可是咱们将线程履行的使命笼统出来,构成使命接口,然后是的线程池与详细的使命无关。

  使命行列:线程池的概念详细到完结则或许是行列,链表之类的数据结构,其间保存履行线程。

  咱们完结的通用线程池结构由五个重要部分组成CThreadManage,CThreadPool,CThread,CJob,CWorkerThread,除此之外结构中还包含线程同步运用的类CThreadMutex和CCondition。

  CJob是一切的使命的基类,其供给一个接口Run,一切的使命类都有必要从该类承继,一起完结Run办法。该办法中完结详细的使命逻辑。

  CThread是Linux中线程的包装,其封装了Linux线程最常常运用的特点和办法,它也是一个笼统类,是一切线程类的基类,具有一个接口Run。

  CWorkerThread是实践被调度和履行的线程类,其从CThread承继而来,完结了CThread中的Run办法。

  CThreadPool是线程池类,其担任保存线程,开释线程以及调度线程。

  CThreadManage是线程池与用户的直接接口,其屏蔽了内部的具i法宣在线体完结。

  CThreadMutex用于线程之间的互斥。

  CCondition则是条件变量的封装,用于线程之间的同步。

  它们的类的承继联系如下图所示: (TO ADD)

  线程池的时序很简略,如下图所示极冰剑豪:(TO ADD)。

  CThreadManage直接跟客户端打交道,其承受需求创立的线程初始个数,并承受客户端提交的使命。这儿的使命是详细的非笼统的使命。CThreadManage的内部实践上调用的都是CThreadPool的相关操作。CThreadPool创立详细的线程,并把客户端提交的使命分发给CWorkerThread,CWorkerThread实践履行详细的使命。

3. 了解体系组件

  下面咱们分隔来了解体系中的各个组件。

  CThreadManage

  CThreadManage的功用十分简略,其供给最简略的办法,其类界说如下:

其间m_Pool指向实践的线程池;m_火车危机圣诞节版NumOfThread是初始创立时分答应创立的并发的线程个数。别的Run和TerminateAll办法也十分简略,仅仅简略的调用CThrea帅哥男同志dPool的一些相关办法罢了。其详细的完结如下:

CThread

  CThread 类实裂组词现了对Linux中线程操作的封装,它是一切线程的基类,也是一个笼统类,供给了一个笼统接口Run,一切的CThread都有必要完结该Run办法。CThread的界说如下所示:

线程的状况能够分为四种,闲暇、繁忙、挂起、停止(包含正常退出和非正常退出)。因为现在Linux线程库不支撑挂起操作,因而,咱们的此处的挂起操作类似于暂停。假如线程创立后不想当即履行使命,那么咱们能够将其“暂停”,假如需求运转,则唤醒。有一点有必要留意的是,一旦线程开端履行使命,将不能被挂起,其将一向履行使命至结束。

  线程类的相关操作均十分简略。线程的履行进口是从Start()函数开端,其将调用函数ThreadFunction,ThreadFunction再调用实践的Run函数,履行实践的使命。

  CThreadPool

  CThreadPool是线程的承载容器,一般能够将其完结为仓库、单向行列或许双向行列。在咱们的体系中咱们运用STL Vector对线程进行保存。CThreadPool的完结代码如下:

class CThreadPool {

friend class CWorkerThread;

private:

unsigned int m_MaxNum; /高宏彬调走/the max thread num that can create at the same ti搞笑一家人,Linux C++线程池结构详解(值得保藏),appme

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;

unsigned int m_InitNum; //Normal thread num;

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;

ve搞笑一家人,Linux C++线程池结构详解(值得保藏),appctor m_ThreadList;

vector m_BusyList; //Thread List

vector m_IdleList; //Idle List

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_Thre搞笑一家人,Linux C++线程池结构详解(值得保藏),appadList.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);

};

CThreadPool::CThreadPool() {

m_MaxNum = 50;

m_AvailLow = 5;

m_InitNum=m_AvailNum = 10 ;

m_AvailHigh = 20;

m_BusyList.clear();

m_IdleList.clear();

for(int i=0;i

CWorkerThread* thr = new CWorkerThrea票预安d();

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

CWorkerThread* thr = new CWorkerThread();

AppendToIdleLis周西的病最新消息t(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 %d/n",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.pu众香堂sh_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::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::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

CWorkerThread* thr = new CWorkerThread();

thr->SetThreadPool(this);

AppendToIdleList(thr);

m_VarMutex.Lock();

m_AvailNum++;

m_VarMutex.Unlock();

thr->Start(); //begin the threa偶的团d,the thread wait for job

}

}

void CThreadPool::DeleteIdleThread(in林河市t num)

{

printf("Enter into CThreadPool::DeleteIdleThread/n");

m_IdleMutex.Lock();

printf("Delete Num is %d/n",num);

for(int i=0;i

CWorkerThread* thr;

if(m_IdleList.size() > 0 ){

thr = (CWork搞笑一家人,Linux C++线程池结构详解(值得保藏),apperThread*)m_IdleList.front();

printf("Get Idle thread %d/n",thr->GetThreadID());

}

vector::iterator pos;

pos = find(m_IdleList.begin(),m_IdleList.e搞笑一家人,Linux C++线程池结构详解(值得保藏),appnd(),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()

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的结构函数中,咱们将履行下面的代码:

在该代码中,咱们将创立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,因而咱们有必要对线程的创立区别对待。

假如创立后总数不超越m_MaxNum,则创立后的线程为m_InitNum;假如超越了,则只创立( m_MaxNum-当时线程总数 )个。

  (3)调用GetIdleThread办法查找闲暇线程。假如当时没有闲暇线程,则挂起;否则将使命指派给该线程,一起将其移入繁忙行列。

当线程履行结束后,其会调用MoveToIdleList办法移入闲暇链表中,其间还调用m_IdleCond.Signal()办法,唤醒GetIdleThread()中或许堵塞的线程。

  CJob

  CJob类相对简略,其封装了使命的根本搞笑一家人,Linux C++线程池结构详解(值得保藏),app的特点和办法,其间最重要的是Run办法,代码如下:

4. 线程池运用示例

  至此咱们给出了一个简略的与详细使命无关的线程池结构。运用该结构十分的简略,咱们所需求的做的便是派生CJob类,将需求完结的使命完结在Run办法中。然后将该Job交由CThreadManage去履行。下面咱们给出一个简略的示例程序:

CXJob和CYJob都是从Job类承继而来,其都完结了Run接口。CXJob仅仅简略的打印一句”The Job comes from CXJob”,CYJob也只打印”The Job comes from CYJob”,然后均休眠2秒钟。在主程序中咱们初始创立10个作业线程。然后别离履行40次CXJob和一次CYJob。

5. 线程池运用跋文

线程池合适场合

  事实上,线程池并不是全能的。它有其特定的运用场合。线程池致力于削减线程自身的开支对使用所发作的影响,这是有条件的,条件便是线程自身开支与线程履行使命比较不行疏忽。假如线程自身的开支相关于线程使命履行开支而言是能够疏忽不计的,那么此刻线程池所带来的优点是不明显的,比方关于FTP服务器以及Telnet服务器,一般传送文件的时刻较长,开支较大,那么此刻,咱们选用线程池未必是抱负的办法,咱们能够挑选“即时创立,即时毁掉”的战略。

  总归线程池一般合适下面的几个场合:

  (1) 单位时刻内处理使命频频并且使命处理时刻短

  (2) 对实时性要求较高。假如承受到使命后在创立线程日驴,或许满意不了实时要求,因而有必要选用线程池进行预创立。

  (3) 有必要常常面临高突发性事情,比方Web服务器,假如有足球转播,则服务器将发作巨大的冲击。此刻假如采纳传统办法,则有必要不断的很多发作线程,毁掉线程。此刻选用动态线程池能够防止这种状况的发作。

注:需求C/C++ Linux服务器开发学习材料私信“材料”(材料包含C/C++,Linux,golang技能,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等),免费共享

版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。