当前位置: 首页 > 科技观察

拆解拆解教你如何设计线程池!还不来学习?

时间:2023-03-21 01:19:55 科技观察

大家好,我是作者小杰。学习线程池的时候查阅过各种资料,感觉写的很好但是不够详细,写的详细设计思路很简单,所以我的出发点是让读者清楚地了解整个设计思路和设计过程,并能举一反三。以后内存池等方面也可以设计得游刃有余!好了正文开始~线程池的设计思路线程池是先打个比方。线程池就像一个工具箱。每次需要拧螺丝的时候,我们都要从工具箱里拿出一把螺丝刀。多人拿出几个来拧,自己的螺丝拧好后,再把螺丝刀放回去,等别人用的时候再拿出来用。也许我的例子还不够完美,但我想我已经基本上把线程池解释清楚了。说白了,线程池相当于提前申请了一些资源,也就是线程。需要的时候,从线程池中取出线程去处理一些东西,处理完再把线程放回去。线程池介绍为什么需要线程池我们来思考一个问题,为什么需要线程池?如果没有线程池,我们每次调用一个线程会是什么样子呢?很明显,先创建一个线程,然后把任务交给这个线程,最后销毁这个线程。所以如果我们改用线程池的话,我们会在程序运行的时候先创建一批线程,然后交给线程池管理。需要的时候取出线程处理任务,不需要的时候回收到线程池中,这样就避免了每次创建和销毁线程的耗时操作。有人会说,你用线程池一开始消耗了一些内存,然后就再也没有释放过内存。是不是有点浪费。其实这类似于用空间换取时间的概念。我们确实多占用了一点内存,但是比起我们珍惜的时间,这段内存还是很划算的。池的概念是一个非常普遍的以空间换时间的概念。除了线程池,还有进程池、内存池等等。其实他们的思路是一样的,就是我先申请一批资源,然后用着用着拿,不放回去。听说这里有一种云计算的思想,他们的道理是一样的。如何设计线程池硬核知识就要开始了,请坐稳扶好扶手~话不多说,我们先看看上图,看看我们要设计什么样的线程池好像!线程池的设计思路我们需要一个线程池类,那么线程池类中需要的东西有哪些呢?我们来看一下,我们需要存储我们创建的线程,所以我们需要一个线程的容器和一个我们的任务的容器。每次有任务放入这个容器,因为是多线程读任务,我们需要加锁。每次读取任务需要加锁和解锁,我们都需要判断什么时候终止,所以我们也需要判断终止,为了避免轮询判断任务容器是否为空,效率太低,所以我们这里用条件变量来解释什么是条件变量。条件变量是并发编程中的一种同步机制。条件变量使线程能够阻塞,直到某个条件发生后才继续执行。在此期间,之前获取的锁会先释放,不会影响其他人对锁的访问。锁。因此,条件变量非常强大和高效。(条件变量和锁在我的多线程一文中会详细讲解,这里不是重点,就不细说了)接下来我们研究一下线程池中需要哪些操作?向线程池Operation中添加任务,此时要通知线程可以取任务执行循环操作,不断等待任务容器中的数据执行,也就是之后需要做什么初始化就是通过改变终止变量来停止上面的循环好了,设计思路到此为止已经写的很详细了。接下来,该看看线程池的实现了。接下来我们看一下线程池类是如何实现的。注释已经很详细了,我就不多说了,直接上代码。classCThreadMangerPool{public:CThreadMangerPool(void):is_running(false){};boolinit(intthreadnum);//初始化函数~CThreadMangerPool(void);voidRun(void);//执行函数voidstop(void);//用于终止LoopfunctionvoidaddTask(ThreadTask*task);//向任务容器添加任务的函数private:boolCreateThreads(intthreadnum=5);std::vector>threadsPool;//Thread容器,用于存放线程std::list>threadTaskList;//任务容器,用于存放线程执行的任务std::condition_variablethreadPool_cv;//条件变量std::mutexthreadMutex;//Mutual拒绝//std::vector>tcpClients;boolis_running;//终止变量};下面来实现几个关键函数~在Run函数中,我们设计了一个循环,不断执行Wait,取出任务执行,如果没有任务可以执行,就sleep等待(通过前面提到的条件变量实现)注意那这里用到了一个方法,我们用while来判断任务容器中的数据是否为空,是的因为类似于进程的shockinggroup现象,所以这里有一个条件变量的假唤醒。(这里不是重点,就不说了,我会在我文章的多线程部分详细讲解)voidCThreadMangerPool::Run(){std::shared_ptrtask;while(true){//循环中std::unique_lockguard(threadMutex);//使用RALL管理锁,无需手动释放while(threadTaskList.empty()){//这里为防止条件变量假唤醒,所以不要用if判断if(!is_runing)break;threadPool_cv.wait(guard);//条件变量的使用}if(!is_runing)//同上,它判断如果没有启动或者调用stop函数,则退出循环break;task=threadTaskList.front();//移除任务threadTaskList.pop_front();//从容器中移除任务if(task==NULL)continue;task->DoIt();//执行任务处理函数task.reset();//重置指针}}接下来看看添加任务的功能是如何实现的voidCThreadMangerPool::addTask(ThreadTask*task){std::shared_ptrptr;//创建指向任务的智能指针ptr.reset(task);{std::lock_guardguard(threadMutex);//同样使用RALL来管理锁,无需手动释放threadTaskList.push_back(ptr);//向任务容器中添加任务}threadPool_cv.notify_all();//通知线程可以执行了,即唤醒刚才在条件变量处休眠的条件}好了,关键函数都看过了,可以实现其他简单的函数,包括初始化函数,终止函数等~