线程池
什么是线程池
- 在使用线程的CS模型中,服务器端每接收到一个客户端请求,都会为客户端创建线程资源,当有大量突发性请求时,服务器来不及为每个客户端创建线程。线程每次的创建与销毁都会耗费服务器大量资源与时间,可以在服务器一开始就创建好一堆线程,等到客户端请求来临直接让这些线程进行处理,这就是线程池。
- 线程池是一种多线程的处理模式,在线程池启动之后,向线程池中添加任务,线程池中的线程将会自动处理这些任务
适用场景
- 需要大量的线程来完成任务,并且每个任务的时间比较短。在WEB服务器上适用线程池是非常有必要的,单个任务量小,任务量大。
- 对性能比较苛刻,需要立即做出应答。
- 接收突发性的大量请求,但是服务器不至于产生大量线程。
简单实现
任务模块:适用setData
为每个对象设置任务,使用Run
来执行任务队列。
struct Task{
public:
void *_data;
public:
bool setData(void *data){
_data = data;
}
bool Run(){
srand(time(NULL));
int sec = rand() % 3;
printf("id:%p -- run data -- %s -- sleep:%d\n", pthread_self(), _data, sec);
sleep(sec);
}
};
线程池模块:
class pthreadPool{
private:
int _max_thr; //max pthread
int _cur_thr; //now pthread
bool _stop_flag; //is stop
std::queue<Task*> _queue;
int _cap; //queue capacity
pthread_mutex_t _lock;
pthread_cond_t _full;
pthread_cond_t _empty;
};
- 使用STL中的队列来模拟实现任务队列,_max_thr表示当前线程池中最大的线程数,
- _cur_thr是当前的线程的数量,_stop_flag是否停止标志,_cap是队列的最大长度。
构造函数:对相关互斥锁和条件变量进行初始化
pthreadPool(int max_thr = 5, int max_queue = 10)
: _max_thr(max_thr)
, _cur_thr(0)
, _stop_flag(false)
, _cap(max_queue)
{
pthread_mutex_init(&_lock, NULL);
pthread_cond_init(&_full, NULL);
pthread_cond_init(&_empty, NULL);
}
初始化线程池:创建线程池支持的最大个数个线程
bool initPool(){
pthread_t tid;
int i, ret;
for(i = 0; i < _max_thr; i++){
ret = pthread_create(&tid, NULL, thrStart, (void*)this);
if(ret != 0){
std::cerr << "pthread_create error" << std::endl;
return false;
}
pthread_detach(tid);
}
}
向线程池中添加任务
bool addTask(Task *task){
pthread_mutex_lock(&_lock);
while(queueIsFull()){
pthread_cond_wait(&_full, &_lock);
}
_queue.push(task);
pthread_cond_signal(&_empty);
pthread_mutex_unlock(&_lock);
return true;
}
- 在每次向线程池中添加任务时,为了防止其他线程获得CPU调度产生的数据安全问题,都需要先对其进行加锁操作
- 如果任务队列满了就需要等待在
_full
条件变量上,此处要用while
循环来判断,而不能用if
- 队列不满,向队列中添加任务,添加完毕之后向等待在
_empty
上的线程做出通知,最后释放锁,添加完毕
线程处理函数
static void *thrStart(void *arg){
pthreadPool *pool = (pthreadPool*)arg;
while(1){
pthread_mutex_lock(&pool->_lock);
//queue is empty, wait
while(pool->queueIsEmpty() && !(pool->_stop_flag)){
pthread_cond_wait(&pool->_empty, &pool->_lock);
}
if(pool->queueIsEmpty() && pool->_stop_flag){
std::cout << "-pthread eixt-" << std::endl;
pool->_cur_thr--;
pthread_mutex_unlock(&pool->_lock);
pthread_cond_signal(&pool->_full);
pthread_exit(NULL);
}
Task* task;
pool->queuePop(&task);
pthread_mutex_unlock(&pool->_lock);
pthread_cond_signal(&pool->_full);
task->Run();
}
}
- 此处线程处理函数声明为静态函数,所以在创建线程时需要把隐含的
this
指针传递过来 - 在进行操作之前先进行加锁操作,如果任务队列为空并且不退出状态就等待在
_empty
上 - 取出任务之后,因为不知道任务具体执行的时间,为了防止死锁,所以先进行解锁操作,通知等待在
_full
上的线程可以添加任务 - 最后执行任务。
判断是否停止线程池
bool stop(){
pthread_mutex_lock(&_lock);
if(_stop_flag){
pthread_mutex_unlock(&_lock);
return false;
}
_stop_flag = true;
while(_cur_thr > 0){
pthread_cond_broadcast(&_empty);
pthread_cond_wait(&_full, &_lock);
}
pthread_mutex_unlock(&_lock);
return false;
}