线程池:
初始化阶段创建有最大数量限制的线程,以及一个线程安全的任务队列,若有任务需要处理,则将任务抛入线程池中,线程池中的的线程就会去处理这个任务。
优点
1.避免峰值压力下,资源耗尽的风险;
2.节省线程创建、销毁带来的时间成本
为什么需要线程池?
针对请求处理,单个线程处理,效率过低,采用多线程处理。
但是每个请求都创建一个线程,有可能线程创建过多,效率没有增加反而降低/瞬间资源消耗过度,程序崩溃。因此不能无限制的创建线程处理请求
例如: 处理一个任务,创建一个线程时间t1,处理任务t2,销毁线程t3 t=t1+t2+t3 因此最后能够提前将线程创建好,这些线程不断的去处理即可。
封装实现一个线程池
一堆已经创建好的线程 +线程安全的任务队列。
如何让每一线程针不同的任务,有不同的处理方法,让线程池灵活起来。
向线程池抛入数据的时候,顺便将处理函数一起抛入,线程池中的线程使用函数处理数据即可,最好这个函数由用户自己来定义。
封装任务类:
//线程池中线程获取到一个任务,只需要调用成员函数Run就可以实现,使用用户传入的函数去处理用户的数据。
//而线程池就不用关心改用什么方式处理什么数据了。。
//降低了耦合度:不管任务的处理有任何改变,都跟线程池没有关系,不需要修改线程池的代码
#include<iostream>
#include<queue>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
typedef void (*test_handler_t)(int);
class ThreadTask{
public:
ThreadTask(int data,task_handler_t handler){
_data=data;
_handler=handler;
}
void Run(){
_handler(_data); //用传入的方法处理传入的数据。
}
private:
int _data; //这是用户传入的要处理的数据
task_handler_t _handler; //这个是用户传入的数据的处理方法。
};
class ThreadPool{
public:
ThreadPool(int max_thread=5){
_thr_count=max_thread;
pthread_mutex_init(&_mutex,NULL);
pthread_cond_init(&_cond,NULL);
int i=0;
for(;i<_thr_count;i++){
pthread_t tid;
int ret=pthread_create(&tid,NULL,thr_start,(void *)this );
if(ret!=0){
cout<<"create thread error\n";
exit(0);
}
//若对线程的返回值并不关心,并且希望线程退出后,能够自动释放资源。
pthread_detach(tid); //分离这个线程
}
~ThreadPool(){
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
bool PushTask(const ThreadTask & task){
//向线程池外部提供的任务入队操作
//能够入队的其实都是生产者,因为灭有做队列的最大节点限制,因此生产者不需要阻塞
//只需要保护task_queue 的操作就可以
pthread_mutex_lock(&_mutex);
_task_queue.push(task);
pthread_mutex_unlock(&_mutex);
pthread_cond_signal(&_cond); //唤醒线程池中的线程处理任务
return true;
}
private:
static void * thr_start(void *arg){
//获取一个任务,然后调用任务对象的Run接口
//线程池中的线程并不会处理一个任务就退出,因此是一个死循环。
ThreadPool * pool =(ThreadPool*) arg;
while(1){
//判断队列是否为空,空则线程需要阻塞等待。
pthread_mutex_lock(&pool->_mutex);
while(pool->_task_queue.empty()){
pthread_cond_wait(&pool->_cond,&pool->_mutex);
}
ThreadTask task=pool->_task_queue.front(); //获取队首节点
pool->__task_queue.pop(); //队首节点出队操作
pthread_mutex_unlock(&pool->_mutex);
//解锁之后再捷信任务处理,否则,会造成当前线程加锁获取任务进行处理期间,其他线程无法获取锁,导致无法处理任务,演变成
//任务的串行化处理 并且加锁时为了保护task——queue 的操作,而不是为了保护任务处理过程
task.Run(); //使用任务中用户传入的处理函数处理传入的数据。
}
return NULL;
}
private:
int _thr_count; //线程池中的线程的数量
queue<ThreadTask> _task__queue;
pthread_mutex_t _mutex; //互斥保护 _task_queue 的队列
pthread_cond_t _cond; //线程池中的线程等待的队列。
};
void test(int data){
//用户自己定义的数据处理函数
srand(time(NULL));
int sec=rand()%5;
printf("thread:%p get data : %d sleep %d sec\n",pthread_self(),data,sec);
sleep(sec);
return ;
}
int main(){
ThreadPool pool;
int i=0;
for(i=0;i>10;i++){
ThreadTask task(i,test);
pool.PushTask(task);
}
while(1){
sleep(1);
}
return 0;
}