目录前言 本文主要学习Linux内核编程,结合VisualStudio2019进行跨平台编程,内容包括线程池介绍以及线程池封装一、线程池介绍 线程池基本概念线程池是预先创建线程的一种技术(服务器真正意义上实现高并发就必须用线程池)举个例子:生活中的水池,是装东西的容器,用来装水的,线程池当然就是拿来装线程的线程池在任务还没有到来之前,创建一定数量的线程,放入空闲队列中,这些线程都是处于阻塞状态,不消耗CPU,但占用较小的内存空间当新任务到来时,缓冲池选择一个空闲线程,把任务传入此线程中运行,如果缓冲池已经没有空闲线程,则新建若干个线程,当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源线程池组成部分线程池类 维护工作者线程队列(包括空闲与忙碌队列) 维护一个任务队列 维护一个线程池调度器指针线程池调度器(本身也是一个线程) 负责线程调度 负责任务分配工作者线程类(线程池中的线程类的封装)任务队列任务接口(实际的业务逻辑都继承自该接口)线程池工作原理 根据服务器的需要,来设置线程的数量,可能是10条、20条、30条,根据服务器的承载,10条不代表只能做10个任务,总有任务做的快,有的做的慢,可能可以完成20个任务 举个例子:如上动图所示,当我们服务器收到一个注册业务,是一个服务器要执行的任务,它会进入到任务队列,队列先进先出,顺次执行,任务会唤醒空闲列表当中的一个空闲的线程,接到任务之后,空闲线程会从空闲列表中消失,进入到忙碌列表,去完成对应的任务,完成任务后,从忙碌列表中出去,到空闲列表继续等待新任务 如果,所有的线程都在忙,都在做任务,这时候登录进来,先进入任务队列,会创建一个新的线程来接这个任务,当所有线程都完成任务,回到空闲列表后,新创建的线程销毁,留下原先设置的对应数量线程(类似,保留老员工,把实习生裁员)队列:先进先出空闲列表(链表):不定长(有的时候可能需要创建新线程来接任务)忙碌列表(链表):不定长(有的时候可能需要创建新线程来接任务) 话不多说,咱们上号,封装一下线程池相关函数,来进行测试 二、线程池代码封装main。cpp主函数,设置10条线程,来执行30个任务includeiostreamincludestdio。hincludeThreadPool。hincludeChildTask。husingnamespacestd;intmain(){ThreadPoolpoolnewThreadPool(10);10条线程for(inti0;i30;i)设置30个任务{charbuf〔40〕{0};初始化sprintf(buf,sd,任务,i);BaseTasktasknewChildTask(buf);poolpushTask(task);}while(1){}return0;}ThreadPool。h对线程池进行设计,核心包括最大、最小线程数,忙碌列表,空闲列表,任务队列,互斥量,条件变量,以及线程执行函数pragmaonceincludequeue队列includelist链表头文件includepthread。h线程头文件includefind查找includeiostreamincludeBaseTask。husingnamespacestd;defineMINNUM10最小值默认参数classThreadPool{public:ThreadPool(constintnumMINNUM);ThreadPool();判断任务队列是否为空boolQueueIsEmpty();线程互斥量加锁解锁voidLock();voidUnlock();线程条件变量等待和唤醒voidWait();voidWakeUp();添加任务到任务队列voidpushTask(BaseTasktask);从任务队列移除任务BaseTaskpopTask(BaseTasktask);从忙碌回到空闲工作结束voidMoveToIdle(pthreadtid);从空闲到忙碌工作开始voidMoveToBusy(pthreadtid);线程执行函数staticvoidRunTime(voidvo);private:intthreadMinNum;最大线程数量intthreadMaxNum;最小线程数量queueBaseTasktaskQueue;任务队列listpthreadtbusyList;线程忙碌列表listpthreadtidleList;线程空闲列表pthreadmutextmutex;互斥量:做锁pthreadcondtcond;条件变量:让线程等待或者唤醒};ThreadPool。cpp对函数进行参数的设置,核心在于线程执行函数上的设置,在工作前和工作完设置打印,方便我们进行观察includeThreadPool。hThreadPool::ThreadPool(constintnum){thisthreadMinNumnum;条件变量、互斥量初始化pthreadmutexinit(thismutex,NULL);pthreadcondinit(thiscond,NULL);pthreadtid;线程num条创建for(inti0;ithisthreadMinNum;i){线程创建pthreadcreate(id,NULL,RunTime,this);thisidleList。pushback(id);线程存入空闲列表}}ThreadPool::ThreadPool(){}任务队列是否为空boolThreadPool::QueueIsEmpty(){returnthistaskQueue。empty();}线程加锁voidThreadPool::Lock(){pthreadmutexlock(thismutex);}线程解锁voidThreadPool::Unlock(){pthreadmutexunlock(thismutex);}线程等待voidThreadPool::Wait(){pthreadcondwait(thiscond,thismutex);}线程唤醒voidThreadPool::WakeUp(){pthreadcondsignal(thiscond);}添加任务到任务队列voidThreadPool::pushTask(BaseTasktask){Lock();taskQueue。push(task);Unlock();WakeUp();}从任务队列移除任务BaseTaskThreadPool::popTask(BaseTasktask){taskthistaskQueue。front();从队列头取thistaskQueue。pop();删除队列头returntask;}从忙碌回到空闲工作结束voidThreadPool::MoveToIdle(pthreadtid){listpthreadt::iteratoriter;iterfind(busyList。begin(),busyList。end(),id);if(iter!busyList。end()){从忙碌移除thisbusyList。erase(iter);添加到空闲thisidleList。pushback(iter);thisidleList。pushback(id)}}从空闲到忙碌工作开始voidThreadPool::MoveToBusy(pthreadtid){listpthreadt::iteratoriter;iterfind(idleList。begin(),idleList。end(),id);if(iter!idleList。end()){从空闲移除thisidleList。erase(iter);添加到忙碌thisbusyList。pushback(iter);thisidleList。pushback(id)}}线程执行函数voidThreadPool::RunTime(voidvo){拿到执行线程自己的id因为后面要处理忙碌和空闲的情况pthreadtidpthreadself();确保主线程与子线程分离,子线程结束后,资源自动回收pthreaddetach(id);线程参数获取ThreadPoolargThis(ThreadPool)vo;while(true){argThisLock();如果任务队列为空线程则一直等待知道任务队列不为空则会被pushTask函数唤醒线程while(argThisQueueIsEmpty()){argThisWait();}argThisMoveToBusy(id);cout工作前任务数:argThistaskQueue。size()endl;cout工作前busy:argThisbusyList。size()endl;cout工作前idle:argThisidleList。size()endl;coutendl;取任务BaseTasktask;taskargThispopTask(task);argThisUnlock();任务工作taskworking();工作结束argThisLock();argThisMoveToIdle(id);argThisUnlock();cout工作完任务数:argThistaskQueue。size()endl;cout工作完busy:argThisbusyList。size()endl;cout工作完idle:argThisidleList。size()endl;coutendl;}returnnullptr;}ChildTask。h子类配置pragmaonceincludeBaseTask。hincludeiostreamincludeunistd。hsleep头文件usingnamespacestd;classChildTask:publicBaseTask{public:ChildTask(chardata);ChildTask();voidworking();};ChildTask。cpp子类设置延时模拟做任务的时间比较长includeChildTask。hChildTask::ChildTask(chardata):BaseTask(data)参数传给父类{}ChildTask::ChildTask(){}voidChildTask::working(){coutthisdata正在执行。。。。。。endl;sleep(3);延时3秒(模拟做业务的时间比较长)}BaseTask。h基类设置结构体来装业务pragmaonceincludestring。hclassBaseTask{public:BaseTask(chardata);BaseTask();chardata〔1024〕;装业务virtualvoidworking()0;虚函数};BaseTask。cpp基类配置includeBaseTask。hBaseTask::BaseTask(chardata){bzero(thisdata,sizeof(thisdata));清空memcpy(thisdata,data,sizeof(data));}BaseTask::BaseTask(){deletethisdata;清除释放}三、测试效果通过Linux连接VS进行跨平台编程,我们可以清晰的看到有几个线程是在做任务,几个线程是空闲的,整个过程就很清晰直观的展现出来了,如下动图所示:10条线程做30个任务的全部记录,如下如所示: 四、总结创建线程池的好处线程池的使用,能让我们搭建的高并发服务器真正意义上做到高并发降低资源消耗 通过重复利用自己创建的线程降低线程创建和销毁造成的消耗提高响应速度 当任务到达时,任务可以不需要等待线程创建和销毁就能立即执行提高线程的可管理性 线程式稀缺资源,如果无限的创建线程,不仅会消耗资源,还会降低系统的稳定性 使用线程池可以进行统一分配,调优和监控