1.线程池概念
线程池:在服务器启动的时候,就创建大量线程+创建一个线程安全的队列作为线程池
任务来了,就将任务泡入线程池的任务队列中,线程池中的一个线程会循环去任务队列中获取任务进行处理
作用:避免线程大量创建/销毁的时间成本;避免资源耗尽的风险
场景:对有大量业务请求进行多任务并行/并返处理
针对每个任务创建线程进行处理存在的问题:
1.若任务过多,有可能创建线程过多,导致资源耗尽
2.每个线程都处理一个任务后提出,会在任务处理整个过程中带来额外的线程创建/销毁成本
2. 线程池实现
#include <cstdio>
#include <iostream>
#include <queue>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
typedef void (*task_handler_t) (int arg);
class ThreadTask
{
public:
ThreadTask() {}
ThreadTask(const int &data, task_handler_t handler):
_data(data), _handler(handler){ }
void SetTask(const int &data, task_handler_t handler) {
_data = data;
_handler = handler;
}
void Run() {
return _handler(_data);
}
private:
int _data;
task_handler_t _handler;
};
#define THREAD_COUNT 5
class ThreadPool
{
public:
ThreadPool(int thr_count = THREAD_COUNT):_max_thr(thr_count){
pthread_mutex_init(&_mutex, NULL);
pthread_cond_init(&_con_pool, NULL);
for (int i = 0; i < _max_thr; i++) {
pthread_t tid;
//void *thread_routine(void *arg);
int ret = pthread_create(&tid, NULL, thr_routine, (void*)this);
if (ret != 0) {
std::cerr << "thread create error\n";
exit(0);
}
pthread_detach(tid);//分离线程--因为我们不关心线程的返回值
}
}
~ThreadPool(){
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_con_pool);
}
bool TashPush(const ThreadTask &task) {
//外部的生产者实现任务入队
//这里线程池任务队列并没有设置最大节点上限,因此不需要判断阻塞
pthread_mutex_lock(&_mutex);
_queue.push(task);
pthread_mutex_unlock(&_mutex);
pthread_cond_signal(&_con_pool);//入队之后唤醒一下线程池中的线程
return true;
}
void QueueLock() {
pthread_mutex_lock(&_mutex);
}
bool QueueIsEmpty() {
return _queue.empty();
}
void ThreadWait() {
pthread_cond_wait(&_con_pool, &_mutex);
}
bool TaskPop(ThreadTask *task) {
*task = _queue.front();
_queue.pop();
return true;
}
void QueueUnlock() {
pthread_mutex_unlock(&_mutex);
}
private:
//这个函数一个类成员函数,有一个隐藏参数this指针,因此需要定义成为静态函数
//这是一个静态函数,没有this指针,导致无法直接访问类的内部成员变量
//通过参数传入当前的对象this指针,进而访问对象的公有成员变量以及成员函数
static void *thr_routine(void *arg) {
ThreadPool *pool = (ThreadPool*)arg;
while(1) {
//循环从队列获取任务进行处理
pool->QueueLock();//类外无法直接访问类的私有成员,因此需要通过公有接口实现操作
while(pool->QueueIsEmpty()) {//访问_queue是否为NULL
pool->ThreadWait(); //访问_con_pool以及_mutex
}
ThreadTask task;
pool->TaskPop(&task); // 访问_queue
pool->QueueUnlock(); // 访问_mutex
//任务的处理一定要放在解锁之后完成,
//否则,如果任务处理时间过长会导致其它线程获取不到任务
task.Run();//通过run接口直接通过用户传入的方法完成数据处理
}
return NULL;
}
private:
int _max_thr;//线程的数量
std::queue<ThreadTask> _queue;//任务队列
pthread_mutex_t _mutex; //实现队列的互斥操作
pthread_cond_t _con_pool;//线程池中的线程等待队列
};
//这个函数就是针对一个数据要传入线程池的处理方法
void test(int data)
{
srand(time(NULL));
int sec = rand()%5;
printf("thread:%p:%d sleep %d sec\n", pthread_self(), data, sec);
sleep(sec);
}
int main()
{
ThreadPool pool;
for (int i = 0; i < 10; i++) {
ThreadTask task;
task.SetTask(i, test);// Run() {test(i)}
pool.TashPush(task);
}
while(1) {
sleep(1);
}
return 0;
}
思路:
1.通过任务类,实现想线程池抛入任务的时候,即抛入任务也抛入处理方法
2.线程池中的线程都只需获取任务对象之后,调用RUn接口就可以实现任务处理
3.线程池只需向外提供一个任务入队接口即可,任务处理都是在线程池内部完成的
注意事项:
1.线程创建函数pthread_create要求传入的入口函数只有一个参数,void(*thread_routine)(void*)
但是若入口函数是一个类的成员函数,则默认会有一个隐藏参数this指针导致函数参数类型不匹配;因此需要将这个入口函数在类内定义为静态函数
2.静态函数无法直接访问线程池 ,因此将这个对象的this指针通过线程入口函数传入,在线程内部就可以访问到这个对象,但是类外使用,因此无法直接访问对象的私用成员,对私有成员的访问都需要通过共有接口来实现