经常听到线程池的概念,不过在实际工作和项目里没有应用过线程池。线程池可能是作为服务端的开发经常用到的技术架构,所以最近我也通过网络简单的学习了下线程池的概念,稍微理解了线程池的应用场景。
在高并发的服务环境下,存在多个客户端访问服务端相同服务的场景,而传统方式,是为多客户端创建一一对应的处理线程,这在高并发场景下极大的耗费服务端的资源。为了解决这类问题,服务端可使用线程池,为同一服务创建相同的线程队列,这些线程循环处理工作队列中的任务,当有客户端需要这种服务时将增加任务到工作队列中。
在实际使用上,可能大家只需要知道如何向工作队列中添加任务即可,不过理解线程池其中的处理流程,可以在遇到问题时更快的理解和定位问题所在,下面通过自己写的一个S端的线程池创建的demo,详细介绍下S端的线程池框架。
首先,前面提到过的,使用线程池需要:线程队列,工作队列,线程池节点。下面是三个对应的结构体
//工作队列节点
struct JOB
{
void (*job_func)(void * args);
int date;
struct JOB* pNext;
struct JOB* pPrev;
};
//线程队列节点
struct WORKER
{
int tid;
struct WORKER* pNext;
struct WORKER* pPrev;
};
//线程池节点
struct Manager
{
struct WORKER* pWorkers;
struct JOB* pJobs;
pthread_mutex_t mtx;
pthred_cond_t cond;
};
/*使用宏定义,实现链表的增加删除节点*/
#define LL_ADD(item, list) \
{
item->pNext = list;
list->pPrev = item;
list = item;
}
#define LL_DEL(item, list) \
{
if(item == list)
item->pNext = item->pPrev = NULL;
item->pPrev->pNext = item->pNext;
item->pNext->pPrev = item->pPrev;
}
初始线程池,包括线程池参数和化线程队列
//线程池初始化
int CreatNthreads(Manager* pMgPool, int workerNum)
{
int i = 0;
if(NULL == pMgPool)
{
return -1;
}
pMgPool->mtx = PTHREAD_MUTEX_INITIALIZER;
pMgPool->cond = PTHREAD_COND_INITIALIZER;
for(i = 0; i < workerNum; i++)
{
WORKER* pWorker = NULL;
malloc(pWorker,sizeof(WORKER));
pthread_creat(&pWorker->tid, NULL, TaskWorkerCallback, pMgPool);
LL_ADD(pWorker, pMgPool->pWorkers);
}
}
实现线程池内部线程接口的逻辑
//线程处理回调
void *TaskWorkerCallback(Manager* pMgPool)
{
JOB* pJob = NULL;
if(NULL == pMgPool)
{
return -1;
}
while(1)
{
memset(pJob, 0, sizeof(Job));
pthread_mutex_lock(&pMgPool->mtx);
while(NULL == pMgPool->pJobs)
pthread_cond_wait(&pMgPool->cond ,&pMgPool->mtx);
pJob = pMgPool->pJobs;
if(pJob)
LL_DEL(pJob,pMgPool->pJobs);
pthread_mutex_unlock(&pMgPool->mtx);
pJob->job_func(pJob->date);
pJob = NULL;
free(pJob);
}
}
前端需要调用postJob()接口向后端工作队列添加节点
//工作真正的处理接口
void func(){}
//向工作队列添加节点
void postJob(Manager* pMgPool,int date)
{
JOB* pJob = NULL;
malloc(pJob,sizeof(JOB));
pthread_mutex_lock(&pMgPool->mtx);
pJob->job_func = func;
pJob->date = date;
LL_ADD(pJob,pMgPool->pJobs);
pthread_mutex_unlock(&pMgPool->mtx);
pthread_cond_signal(&pMgPool->cond);
}
以上方式实现的工作队列链表是“类栈链表”,及后来的节点,却优先处理。和栈的出栈入栈方式类似,我这里先叫“类栈链表”吧,到这里基本上实现了线程栈的创建和使用流程,具体在根据应用场景,实现func即可;
缺点:如果是需要响应C端的服务,使用线程池可能会影响响应速度。(不需要响应的,比如客户端发起的会导致写硬盘的操作)
优点:高并发场景下,极大降低了S端的性能需求
(以上,均为作者个人理解,如有错误支持,欢迎评论指导)