文章目录
1. 什么是线程池?
线程池: 一种线程使用模式,一个程序中线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配,可并发执行的任务。
1.1 线程池的本质
线程池 = 线程安全队列 + 一大堆的线程
线程安全队列:元素 = 数据 + 处理数据的函数地址
优点:
- 线程池维护了多个线程,避免数据某时刻从网络中来到时,短时间创建和销毁线程带来的代价。
- 线程池不仅能够保证内核的充分利用,还能防止过度调度。
- 可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
应用场景:
- 需要大量线程完成代码,且每段代码的执行时间比较短
- 对性能要求苛刻,例如要求服务器能迅速响应客户请求
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用
1.2 线程池处理数据流程
上图流程: 从网络来的数据,直接调用push插入线程池队列,然后在处理完成之后,再发送回,发送队列,之后发送线程,回到网络通过了一个队列(也可以叫缓冲),优点:把前台和后端解耦开来
注意:
- 线程池中的线程,都是同一种角色的线程,也就是每个线程都执行同样的入口函数。
- 如果线程池中的线程都是执行同样一个入口函数的话,执行的功能是不是就比较单一了?如何满足大量的业务需求呢?
上面问题简化也就是:如何让相同入口函数的线程,处理不同的请求?
1.switch case:遇到不同数据,case不同方法,处理大量不同需求时,比较麻烦
2.数据函数都抛入:向线程池抛入数据的时候,将处理该数据的函数地址和数据一起抛入,线程池当中的线程,只需要调用传入的函数去进行处理数据即可。
因此:线程安全队列当中的元素:数据 + 处理函数的地址 handler_
1.3 线程安全队列元素类型
- 函数指针定义:
typedef void* (*Handler_t)(int);
返回值为void* 参数为int - C++中线程的入口函数一定要
static
修饰的,因为线程入口函数规定参数只能有一个参数void* arg
,而类里成员函数有隐含的this指针,不符合要求
2. 模拟实现处理网络数据
上面的图可以帮助理解代码
#include<stdio.h>
#include<unistd.h>
#include<queue>
#include<pthread.h>
#include<iostream>
#define THREADCOUNT 4
typedef void (*Handler_t)(int); 函数指针类型
class ThreadTask 队列的元素类型
{
private:
Handler_t handler_; 保存处理数据的函数
int data_; 待处理的数据
public:
ThreadTask(Handler_t handler, int data)
:handler_(handler)
, data_(data)
{
}
~ThreadTask()
{
}
void Run() 直接跑函数,处理数据。
{
handler_(data_);
}
};
class ThreadPool
{
private:
std::queue<ThreadTask