多线程技术
多线程技术主要解决处理器单元内多个线程执行的问题,它可以使处理器尽量保持忙碌状态。在C/S模式的服务器–客户端通信中应用多线程技术,即是服务器在Accept调用等待客户端连接成功时,为每一个客户端连接创建一个线程来处理客户请求。在这种情况下,每次客户连接时,服务器都会进行线程创建、任务分配;客户请求完成后,服务器还需要销毁线程,这一系列的工作需要消耗很多时间,尤其是在大量用户并发时此矛盾尤为突出。例如,一个用户请求连接,处理器则为它创建一个线程;此时另一个用户请求连接,这时这个用户只能等待处理器完成上一个线程创建之后才能连接成功。
线程池
线程执行时间
线程执行过程分为三个部分:T1:线程创建的时间,T2:线程执行任务所需时间,包括线程的同步等时间;T3线程销毁时间。总计线程完成一项恩物的时间为T1、T2、T3之和。真正处理任务的时间为T2,这样线程开销占总时间的比例为(T1+T3)/(T1+T2+T3),当线程任务处理时间T2较小时,那么开销所占比例将趋近于1,这也正是多线程技术的瓶颈所在。
线程池的优点
线程池的实现着眼于减少T1和T3这两部分时间的开销,使得系统的效率提高。服务器通过预先创建一定数量的工作线程并限制其数量,等到客户端发起连接请求时,直接从线程池分配一个线程用于客户端请求。
1. 可以控制预先产生的线程的数量,也就是线程池的大小,从而达到控制线程对象的内存消耗。
2. 降低系统开销和资源消耗。通过对多个请求重用线程,线程创建和销毁的开销被分配到了多个请求上。
3. 提高系统的响应速度。
线程池的基本原理
线程池主要包括以下几个组成部分:
- 线程管理器:用于创建并管理线程池
- 工作线程:线程池中实际执行任务的线程。
- 任务接口:每个任务必须实现的接口,当可执行任务到来时,线程池中的工作线程将调试执行这个任务。把任务抽象为任务接口,可以做到线程池与具体的任务无关。
- 任务队列:用来存放没有处理的任务,提供一种缓冲机制。常用队列来实现。
线程池的具体实现
线程池的具体实现借鉴了《线程池技术在并发服务器中的应用》论文所述。
数据结构定义
typedef struct worker{ //任务接口
//回调函数,任务运行时会调用此函数,注意也可声明成其它形式
void *(*process) (void *arg);
void *arg;//回调函数的参数
struct worker *next;
}CThread_worker;
typedef struct {
//线程管理器
pthread_mutex_t queue_lock; //共享资源互斥锁
pthread_cond_t queue_ready;
CThread_worker *queue_head;//任务队列
int shutdown;//是否销毁线程池
pthread_t *threadid; //工作线程引用
int max_thread_num;
//当前等待队列的任务数目
int cur_queue_size;
}CThread_pool;
线程池相关函数
int pool_add_worker (void *(*process) (void *arg), void *arg);
void *thread_routine (void *arg);
static CThread_pool *pool = NULL;
void
pool_init (int max_thread_num){
pool = (CThread_pool *) malloc (sizeof (CThread_pool));
pthread_mutex_init (&(pool->queue_lock), NULL);
pthread_cond_init (&(pool->queue_ready), NULL);
pool->queue_head = NULL;
pool->max_thread_num = max_thread_num;
pool->cur_queue_size = 0;
pool->shutdown = 0;
pool->threadid =
(pthread_t *) malloc (max_thread_num * sizeof (pthread_t));