高并发中的线程与线程池( 四 )


线程池是如何工作的可能有的同学会问,该怎么给线程池提交任务呢?这些任务又是怎么给到线程池中线程呢?
很显然,数据结构中的队列天然适合这种场景,提交任务的就是生产者,消费任务的线程就是消费者,实际上这就是经典的生产者-消费者问题 。

高并发中的线程与线程池

文章插图
图片
现在你应该知道为什么操作系统课程要讲、面试要问这个问题了吧,因为如果你对生产者-消费者问题不理解的话,本质上你是无法正确的写出线程池的 。
限于篇幅在这里博主不打算详细的讲解生产者消费者问题,参考操作系统相关资料就能获取答案 。这里博主打算讲一讲一般提交给线程池的任务是什么样子的 。
一般来说提交给线程池的任务包含两部分:1) 需要被处理的数据;2) 处理数据的函数
struct task {void* data;// 任务所携带的数据handler handle; // 处理数据的方法}(注意,你也可以把代码中的struct理解成class,也就是对象 。)
线程池中的线程会阻塞在队列上,当生产者向队列中写入数据后,线程池中的某个线程会被唤醒,该线程从队列中取出上述结构体(或者对象),以结构体(或者对象)中的数据为参数并调用处理函数:
while(true) {struct task = GetFromQueue(); // 从队列中取出数据task->handle(task->data);// 处理数据} 以上就是线程池最核心的部分 。
理解这些你就能明白线程池是如何工作的了 。
线程池中线程的数量现在线程池有了,那么线程池中线程的数量该是多少呢?
在接着往下看前先自己想一想这个问题 。
如果你能看到这里说明还没有睡着 。
要知道线程池的线程过少就不能充分利用CPU,线程创建的过多反而会造成系统性能下降,内存占用过多,线程切换造成的消耗等等 。因此线程的数量既不能太多也不能太少,那到底该是多少呢?
回答这个问题,你需要知道线程池处理的任务有哪几类,有的同学可能会说你不是说有两类吗?长任务和短任务,这个是从生命周期的角度来看的,那么从处理任务所需要的资源角度看也有两种类型,这就是没事儿找抽型和 。。啊不,是CPU密集型和I/O密集型 。
CPU密集型所谓CPU密集型就是说处理任务不需要依赖外部I/O,比如科学计算、矩阵运算等等 。在这种情况下只要线程的数量和核数基本相同就可以充分利用CPU资源 。




推荐阅读