1. 背景
什么是线程池?
线程池技术是池化技术的一种。除了线程池,还是内存池、连接池等其他池化技术。打个比方来说,线程池是将若干个随时可以执行任务的线程放在“池子”这种容器中,当我们要使用线程的时候,从线程池中取出即可;使用完成后再将线程归还给线程池,以便下次或者其他用户使用。
本项目为什么需要线程池?
使用线程池技术的最大好处是服务器可以避免因重复的建立和销毁线程带来的开销,从而提高服务器对客户端的响应速度。
此外,一个线程从诞生到结束,在系统中存在的时间可以分成三个阶段:创建耗时t1
, 工作时间t2
,销毁耗时t3
。
n = t 2 t 1 + t 2 + t 3 n = \frac{t2}{t1+t2+t3} n=t1+t2+t3t2
为了提高服务器的效率,我们应该尽可能的增大t2
,减少t1
和t3
。但一般来说,减少t1
和t3
是一个比较困难的事情,所以自然而然的会想到提高t2
的时间。
目前,线程池技术的应用已经是一件很常见的事情了。该技术原理并不复杂,实现起来也较为容易。对于网络编程人员来说,这是一个必须得掌握的知识。
2. 实现
在编写一个线程池类之前,你需要具备以下知识:
- Linux多线程编程;
- 常见的数据结构;
- C++11编程;
在理解了线程池的工作原理后,自己动手实现一个并不算难。下面po出一张线程池的原理示意图:
线程池类设计要点:
- 如何定义线程需要去执行的“任务”(Tasks):
- 首先要明确的一点是,“任务”是函数。无论你是使用Linux C的pthread库,还是C++的库,创建一个线程,为了让线程工作,都需要向线程传递一个函数。
- 然而,这个函数是需要预先声明和定义的。但是在线程池工作的时候,如果我们想要线程执行不同功能的函数,不可能提前知道并声明和定义好。所以,为了让线程执行不同的函数,传递给线程的函数中,会调用其他的函数以实现执行正确的任务;
- 选择合适的容器:对于线程,数组这类容器就能满足要求;而对于工作队列(Task Queue),可以选择队列或者链表。
- 正确的上锁:本质上,线程池类可以简化成生产者/消费者模型。线程执行task是在消耗资源,往线程池中添加task是在生产资源。这里的资源指的就是需要执行的函数。生产者/消费者模型中,有好几种上锁的方式:互斥锁+条件变量、信号量等。选择一种你习惯使用的就行。
如果你理解到线程池的本质是生产者/消费者模型后,其实如何编写这个类已经清晰很多了。你可能对于如何向线程池中添加不同的函数还感到疑惑,别着急,接着往下看。
我们可以把函数看做是一个对象&#x