一. 什么是线程池?:
诸如web服务器、数据库服务器、文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务。构建服务器应用程序的一个过于简单的模型是:每当一个请求到达就创建一个新的服务对象,然后在新的服务对象中为请求服务。但当有大量请求并发访问时,服务器不断的创建和销毁对象的开销很大。所以提高服务器效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这样就引入了“池”的概念,“池”的概念使得人们可以定制一定量的资源,然后对这些资源进行复用,而不是频繁的创建和销毁。
线程池是预先创建线程的一种技术。线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中。这些线程都是处于睡眠状态,即均为启动,不消耗CPU,而只是占用较小的内存空间。当请求到来之后,缓冲池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求。当系统比较闲的时候,也可以通过移除一部分一直处于停用状态的线程。
二. 线程池的注意事项
(1)线程池大小。多线程应用并非线程越多越好,需要根据系统运行的软硬件环境以及应用本身的特点决定线程池的大小。一般来说,如果代码结构合理的话,线程数目与CPU 数量相适合即可。如果线程运行时可能出现阻塞现象,可相应增加池的大小;如有必要可采用自适应算法来动态调整线程池的大小,以提高CPU 的有效利用率和系统的整体性能。
(2)并发错误。多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
(3)线程泄漏。这是线程池应用中一个严重的问题,当任务执行完毕而线程没能返回池中就会发生线程泄漏现象。
三. 线程池的设计
(1)threadpool_init()函数: 完成线程池的初始化工作,包括初始化任务队列、初始化条件变量和互斥锁、初始化线程、并给线程注册thread_worker()函数
(2)threadpool_add()函数: 向任务队列中添加任务,包括线程实际执行的函数指针和所需的参数,并将任务队列长度加一。
(3)threadpool_worker()函数: 实际工作函数,该函数是一个while循环,进入循环后先判断任务队列长度,如果任务队列长度为零,则阻塞在pthread_cond_wait调用,如果接收到信号或者进入函数时队列长度不为零,则从任务队列中取出一个任务,队列长度减一,然后执行任务。
(4)threadpool_destroy()函数: 销毁线程池,等待子线程结束后,销毁各种资源。
//threadpool.h
#include <pthread.h>
#include <stdlib.h>
struct threadpool_task{
void (*func)(void *); //函数指针
void *arg; //参数
struct threadpool_task *next; //指向下一个任务
};
struct threadpool_t{
pthread_mutex_t lock; //互斥锁
pthread_cond_t cond; //条件变量
pthread_t* threads; //线程ID指针
bool shutdown;//线程退出标志
threadpool_task *head; //指向任务队列头指针
int queue_size;
int threadNum;
};
threadpool_t *threadpool_init(int threadNum); //初始化
int threadpool_add(threadpool_t *pt, void (*func)(void*), void *arg); //生产者
int threadpool_destroy(threadpool_t *pt);
int threadpool_gettasksize(threadpool_t* pool);
#include "threadpool.h"
using namespace std;
static void *threadpool_worker(void *arg);
threadpool_t *threadpool_init(int threadNum)
{
threadpool_t *pool;
if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) return NULL;
//执行初始化
pthread_mutex_init(&(pool->lock), NULL);
pthread_cond_init(&(pool->cond), NULL);
//头指针
pool->head = (threadpool_task *)malloc(sizeof(threadpool_task));
pool->head->func = NULL;
pool->head->arg = NULL;
pool->head->next = NULL;
pool->queue_size = 0;
pool->shutdown = false;
pool->threadNum = threadNum;
pool->threads = (pthread_t *)malloc(threadNum*(sizeof(pthread_t)));
if(pool->threads == NULL) cerr << "pool->threads" << endl;
for(int i = 0; i < threadNum; i++)
pthread_create(&(pool->threads[i]), NULL, threadpool_worker, (void *)pool);
return pool;
}
//生产者建立一个任务并添加到任务队列中
int threadpool_add(threadpool_t *pool, void (*func)(void *), void *arg)
{
threadpool_task *task = (threadpool_task *)malloc(sizeof(threadpool_task));
if(task == NULL) cerr << "task" << endl;
task->func = func;
task->arg = arg;
pthread_mutex_lock(&(pool->lock));
//头插法添加任务到任务队列中
task->next = pool->head->next;
pool->head->next = task;
pool->queue_size++;
pthread_mutex_unlock(&pool->lock);
//发送信号唤醒工作线程
pthread_cond_signal(&(pool->cond));
pthread_mutex_unlock(&pool->lock);
return 0;
}
//消费者处理线程
static void *threadpool_worker(void *arg)
{
threadpool_t *pool = (threadpool_t *)arg;
threadpool_task *task;
pthread_t tid = pthread_self();
while(1)
{
pthread_mutex_lock(&pool->lock); //加锁
while(pool->queue_size == 0 && !pool->shutdown) //任务队列为空且没有退出线程的命令
pthread_cond_wait(&(pool->cond), &(pool->lock)); //解锁-wait-收到信号-加锁-返回
if(pool->shutdown)
{
pthread_mutex_unlock(&(pool->lock));
pthread_exit(NULL);
}
task = pool->head->next;
if(task == NULL) continue;
pool->head->next = task->next;
pool->queue_size--;
pthread_mutex_unlock(&(pool->lock));
task->func(task->arg);
free(task);
}
pthread_mutex_unlock(&(pool->lock));
pthread_exit(NULL);
return NULL;
}
int threadpool_destroy(threadpool_t* pool)
{
if(pool->shutdown) return -1;
pool->shutdown = true;
pthread_cond_broadcast(&(pool->cond));
for(int i = 0; i < pool->threadNum; i++) pthread_join(pool->threads[i], NULL);
free(pool->threads);
pool->threads = NULL;
pthread_mutex_destroy(&(pool->lock));
pthread_cond_destroy(&(pool->cond));
return 0;
}
int threadpool_gettasksize(threadpool_t* pool)
{
return pool->queue_size;
}