1.线程池的基本原理
在传统服务器结构中, 每当有一个新的用户进入, 服务器就开启一个新的线程用户处理这 个用户的数据包。这个线程只服务于这个用户 , 当 用户与服务器端关闭连接以后,服务器端销毁这个线程。然而频繁地开辟与销毁线程极大地占用了系统的资源。 线程池的基本思想就是在程序 开始时就在内存中开辟一些线程, 线程的数目是 固定的,他们独自形成一个类,屏蔽了对外的操作, 而服务器只需要将数据包交给线程池就可以了。当有新的客户请求到达时 , 从“池子”中选择一个空闲的线程为新的客户请求服务 ,服务完毕后 , 线程进入空闲线程池中。如果没有线程空闲 的 话, 就 将 数据 包 暂 时 积 累 , 等 待 线 程 池 内 有 线 程空闲以后再进行处理。不仅降低了对线程对象创建和销毁的开销,也可 以 提 高 请 求 的响应时间 。
2.线程池的组成部分
- 线程池管理器:用于创建并管理线程池,包括创建,销毁,添加等
- 工作线程:线程池中实际执行任务的线程。在初始化线程时会预先创建好固定数目的线程在池中,处于空闲状态的时候,一般不占用CPU,占用较小的内存空间。
- 任务队列:用于存放没有处理的任务。提供一种缓冲机制。一般是队列或者链表
- 任务接口:每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
3.线程池的使用
什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,就可以使用线程池。
并且由于多个线程都会操作任务队列,包括查询和删除任务节点,线程管理器添加的时候也需要操作任务队列,因此需要实现同步与互斥操作。
4.示例代码
有关互斥锁和条件变量的配合使用参见条件变量和互斥锁
本实验的代码使用来了互斥锁和条件变量实现了线程间同步和互斥的机制。在运行过程中,一个线程池对象被创建以后,会生成100个线程同时等待任务队列状态的变化,当任务队列被插进任务的时候会向条件等待中的线程发送条件,被条件激活的线程在获取互斥量之后,判断任务队列,取任务执行,或者继续等待。流程图如下:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct task //任务结构体
{
void *(*func) (void *);
void *arg;
struct task * next;
}TASK;
typedef struct pool //线程池结构体
{
TASK * taskhead;
pthread_mutex_t tasklock; // 初始化一个互斥锁
pthread_t *tids; //线程ID集(动态申请)
pthread_cond_t dotaskcond; //初始化一个条件变量
}POOL;
void *dotask(void *arg); //线程的主体函数
POOL *pool_create(int nthread); //创建线程池
int pool_add_task(POOL *pool, void *(*taskfunc)(void *), void *taskarg); //添加任务
void * show_int(void *arg); //添加的任务函数
int main(void)
{
int n = 100;
POOL *mypool = pool_create(100); //创建一个线程池并添加100个线程
while(n--)
{
pool_add_task(mypool,show_int,(void *)(long)n); //向等待队列中添加任务让线程去执行
}
pause();
return 0;
}
void *dotask(void *arg) //线程的主体函数
{
POOL *pool = (POOL *)arg;
while(1)
{
pthread_mutex_lock(&pool->tasklock); //上锁
//如果当前任务队列是空的,那么继续等待
while(NULL == pool->taskhead)
{
pthread_cond_wait(&pool->dotaskcond, &pool->tasklock);
}
TASK *mytask = pool->taskhead;
pool->taskhead = mytask->next;
pthread_mutex_unlock(&pool->tasklock);
mytask->func(mytask->arg);
free(mytask);
}
}
POOL *pool_create(int nthread) //创建一个线程池
{
POOL *pool = (POOL *)malloc(sizeof(POOL));
pool->taskhead = NULL;
pthread_mutex_init(&pool->tasklock, NULL); //初始化互斥锁
pool->tids = (pthread_t *)malloc(sizeof(pthread_t) * nthread); //动态申请线程ID集合
pthread_cond_init(&pool->dotaskcond, NULL); //初始化条件变量
while(nthread--) //创建100个线程
{
pthread_create(&pool->tids[nthread], NULL, dotask, pool);
}
return pool;
}
int pool_add_task(POOL *pool, void *(*taskfunc)(void *), void *taskarg)
{
TASK *mytask = (TASK *)malloc(sizeof(TASK));
//获取任务的信息
mytask->func = taskfunc;
mytask->arg = taskarg;
mytask->next = NULL;
//操作任务队列,将任务添加到任务队列
pthread_mutex_lock(&pool->tasklock);
mytask->next = pool->taskhead;
pool->taskhead = mytask;
pthread_mutex_unlock(&pool->tasklock);
//发送cond通知等待中的线程
pthread_cond_signal(&pool->dotaskcond);
}
void *show_int(void *arg)
{
//打印线程ID和收到的arg的值
printf("[%lu]%d\n",pthread_self(), (int)(long)arg);
sleep(1);
}
运行结果: