【三】通用线程池--构建

(1)思想
    1.一个进程中的线程就好比是一家公司里的员工,员工的数目应该根据公司的业务多少来
      定,太少了忙不过来,但是太多了也浪费资源。    
    2.最理想的情况是:让进程有一些初始数目的线程(所谓的线程池),当没有任务的时候这些线程自动进入睡眠,
      有了任务他们会立即执行任务,不断循环。
    3.进程还应该可以根据自身任务的繁重与否来增删线程的数目。
    4.当所有的任务都完成了之后,所有的线程还能妥当地收官走人,不带走一片云彩。
    5.在实际应用过程中, 将线程池编译成库。
    
(2)达到的目的    
    1.用户通过init_pool(&pool, n)来初始化一个线程池, 初始线程数为5n
    2.用户通过add_task(&pool,do_task, "arg")来向任务链表中投递任务
    3.当n个线程能及时处理任务链表里面的任务时(任务链表没有任务排队), 空闲的线程休眠, 新任务添加时唤醒
    4.当n个线程不能及时处理任务链表里面的任务时(任务链表有任务排队), 创建新线程来执行任务
    5.能创建的线程数上线为MAX个
    6.当任务不再繁忙时, 要取消额外创建的线程

(3)框架构想
        主线程:                         投递任务
                                       ↓(投递任务)    
        任务链表:    任务1-->任务2->任务3...................
                    ↓    
                    ↓(取出第一个任务)
            |-------------------------|
            |线程1    线程2    线程3 ... |
            |-------------------------|
    1.任务链表被多个线程操作, 需要提供互斥锁来保护"共享资源"     
    2.限制创建线程的最大个数
    3.向外提供初始化线程池的接口
        1.根据用户的要求创建初始线程个数为n的线程池
        2.初始化一个任务链表
    4.内部线程池的线程处理函数
        上锁    // 如果这里不上锁,有可能在判断过程中添加了新任务
            注册线程清理函数(防止线程被取消时,没有释放互斥锁)
            1.任务链表为空:
                当前线程个数 >= 用户初始创建的线程个数?
                否: 当前线程睡眠在条件变量上(while + wait)
                是: 关闭当前线程    (现在任务不繁忙, 空闲线程占用系统资源)
            2.任务链表不为空:
                    1.当前线程从链表中取出第一个任务
                    2.从任务链表中删除当前任务
        解锁    // 先解锁后执行的原因是: 任务执行期间不应该阻止其他线程获取任务
        3.执行任务
        4.当前任务执行完毕后, 重复上面的操作
    5.向外提供主线程投递任务到任务链表的接口
        上锁
            1.将任务投递到链表尾部
            2.当前有正在睡眠的线程?
                有: 广播所有在条件变量上睡眠的线程
                无: 创建线程        // 当前任务繁忙, 用户要求创建的线程不够用
        解锁
            用新建线程执行该任务
    6.向外提供销毁线程池的接口

(4) 源代码及测试程序

my_pthread_pool.h

#ifndef MY_PTHREAD_POOL_H
#define MY_PTHREAD_POOL_H
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <pthread.h>

#define CUR printf("%s: %d\n", __FILE__, __LINE__)
#define pr printf

//#define CUR 
//#define PR

#define PTHREAD_MAX	20
typedef void *(*task)(void *arg);

typedef unsigned int	   u32;
typedef unsigned short	   u16;
typedef unsigned char	   u8;

// 任务链表
typedef struct task {
	task do_task;			// 定义了一个void *(*)(void *)的函数指针
	void *arg;
	struct task *next;
}task_t;

// tid链表
typedef struct tid_info {
	pthread_t tid;			// 定义了一个void *(*)(void *)的函数指针
	pthread_attr_t attr;
	struct tid_info *next;
}tid_info_t;

typedef struct thread_pool {
	task_t *task_list;
	tid_info_t *tid_info_list;
	u32 n_cur_task;					// 链表中任务个数
	u32 n_cur_thread;				// 当前存在的线程个数
	u32 n_usr_thread;				// 用户要求创建的线程个数
	u32 n_wait_thread;				// 当前正在睡眠的线程个数
	
	pthread_attr_t attr;	
	pthread_cond_t cond;
	pthread_mutex_t mutex;
}thread_pool_t;


int destory_pool(thread_pool_t *pool);
int add_task(thread_pool_t *pool, task do_task, void *arg);
int init_pool(thread_pool_t *pool, u32 pthread_num);



#endif  //MY_PTHREAD_POOL_H

my_pthread_pool.c


#include "my_pthread_pool.h"
void add_tid_info(thread_pool_t *pool, tid_info_t *new_info)
{
	tid_info_t *tmp = pool->tid_info_list;	
	if (!tmp)	{
		pool->tid_info_list = new_info;	
		pool->tid_info_list->next = NULL;
	}
	else {
		while(tmp && tmp->next)	 
			tmp = tmp->next;
		tmp->next = new_info;	// tmp是链表的最后一项
		tmp->next->next = NULL;
	}
}

// 线程的清理函数
static void clean(void *arg)
{
	thread_pool_t *pool = (thread_pool_t *)arg;
	pthread_mutex_unlock(&pool->mutex);
}

static void cancel_tid_pthread(thread_pool_t *pool, pthread_t tid)
{	
	tid_info_t *tmp = pool->tid_info_list;	// 链表头
	tid_info_t *pre;	// 链表头
	
	// 关闭线程
	pthread_cancel(tid);
	pool->n_cur_thread--;
	printf("cancel: n_cur_thread = %d\n", pool->n_cur_thread);
	if (tid == tmp->tid){			// 该线程tid信息是链表中的第一项
		pool->tid_info_list = tmp->next;  // 链表头改为第二项	
		goto free;
	}
	// 在链表中找到tid_info结构, 删除
	while(tmp) {
		if(tid == tmp->tid)	// tmp项是要删除的结构
			break;
		pre = tmp;
		tmp = tmp->next;
	}
	pre->next = tmp->next; // tmp的上一项指向tmp的下一项

	// 释放tid_info结构
free:
	free(tmp);

}


// 线程处理函数, 传入线程池
static void *work(void *arg)
{
	thread_pool_t *pool = (thread_pool_t *)arg;
	task_t *head;
	while(1)	{
		// 1.注册线程清理函数
		pthread_cleanup_push(clean, (void *)pool);
		pthread_mutex_lock(&pool->mutex);

		// 2.当前没有任务要处理
		if (0 == pool->n_cur_task){
			// 当前线程个数 > 用户初始创建的线程个数
			if (pool->n_cur_thread > pool->n_usr_thread)	
				cancel_tid_pthread(pool, pthread_self());	// 关闭当前线程。任务不繁忙, 不需要这么多线程
			// 当前线程个数 <= 用户初始创建的线程个数	
			else {	// 当前线程睡眠条件变量cond上
				pool->n_wait_thread++;			// 当前正在睡眠的线程个数	
				pr("n_wait_thread = %d\n", pool->n_wait_thread);
				pthread_cond_wait(&pool->cond, &pool->mutex);
				while(0 == pool->n_cur_task)	// 存在假唤醒情况, 所以加了while(0 == pool->n_cur_task)
					pthread_cond_wait(&pool->cond, &pool->mutex);
				pool->n_wait_thread --;			// 当前正在睡眠的线程个数	
				pr("n_wait_thread = %d\n", pool->n_wait_thread);
			}
		}
		// 3.当前有任务要处理或者被唤醒
		head = pool->task_list;			// 取出任务链表中的第一个任务
		pool->task_list = head->next;	// 从链表中删除任务
		pool->n_cur_task--;				// 链表中的任务个数
		pr("task_list:    = %d\n", pool->n_cur_task);
		pr("npthread:     = %d\n", pool->n_cur_thread);
		pthread_mutex_unlock(&pool->mutex);
		pthread_cleanup_pop(0);	 // 执行到这里,锁已经被解开了, 不用执行清理函数
		// 执行任务
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
		head->do_task(head->arg);
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
		// 取消禁止
		free(head);		// head已经从链表中删除了, 不是共享数据
		//sleep(1);
	}
	pthread_exit(NULL);
}


// 创建分离线程, 传入创建的个数
static u32 create_detach_pthread(thread_pool_t *pool, int num)
{	
	int i;
	tid_info_t *new_info;
	for (i = 0; i < num; i++)	{
		new_info = malloc(sizeof(tid_info_t));
		if (!new_info) {
			perror("malloc err");
			return -1;
		}
		pthread_attr_init(&new_info->attr);
		pthread_attr_setdetachstate(&new_info->attr,PTHREAD_CREATE_DETACHED); // 设置线程的分离属性
		// 创建线程来处理任务
		if(pthread_create(&new_info->tid, &new_info->attr, work, (void *)pool)) { 
			perror("ERR: creat pthread");
			return -1;
		}
		// 将tid_info挂入链表
		add_tid_info(pool, new_info);
		pool->n_cur_thread++;	// 当前存在的线程个数
	}
	return 0;
}
/* 传入线程池指针, 要创建的线程个数,成功返回0,失败返回-1 */ 
int init_pool(thread_pool_t *pool, u32 pthread_num)
{	
	int i, ret;
	tid_info_t *new_info;
	
	// 1.初始化条件变量和互斥锁
	pthread_mutex_init(&pool->mutex, NULL);
	pthread_cond_init(&pool->cond, NULL);
	pthread_mutex_lock(&pool->mutex);
	// 2.初始化task_list, tid_info_list
	pool->task_list = NULL;
	pool->tid_info_list = NULL;
	
	// 3.创建要求个数的线程
	if (pthread_num > PTHREAD_MAX)
		pthread_num = PTHREAD_MAX;
	pool->n_usr_thread = pthread_num;	// 用户要求创建的线程个数
	pool->n_wait_thread = 0;			// 当前正在睡眠的线程个数	
	pool->n_cur_thread = 0;				// 当前存在的线程个数
	pool->n_cur_task = 0;				// 初始任务个数
	if (-1 == create_detach_pthread(pool, pool->n_usr_thread)) 
		return -1;
	pthread_mutex_unlock(&pool->mutex);
	return 0;
}

/* 成功返回添加后链表中的任务个数, 失败返回-1; */ 
int add_task(thread_pool_t *pool, task do_task, void *arg)
{	
	task_t *tmp = pool->task_list;	// 链表头
	
	// 构建一个新任务
	task_t *new_task = malloc(sizeof(task_t));
	new_task->do_task = do_task;
	new_task->arg     = arg;

	pthread_mutex_lock(&pool->mutex);
	// 1.将任务投递到链表尾部
	if (NULL == tmp)	{			// 链表为空
		pool->task_list = new_task;
		pool->task_list->next = NULL;
	}
	else {
		while(tmp && tmp->next)
			tmp = tmp->next;
		tmp->next = new_task;	// tmp是链表的最后一项
		tmp->next->next = NULL;
	}
	
	pool->n_cur_task++;	// 当前存在的任务个数
	pr("add_task: n_cur_task = %d\n", pool->n_cur_task);
	// cond上有线程正在睡眠
	if (pool->n_wait_thread > 0)				
		pthread_cond_broadcast(&pool->cond);		// 广播所有睡眠在cond上的线程
	// cond上没有线程在睡眠	
	else {
		if (pool->n_cur_thread < PTHREAD_MAX) {		// 如果没有超过最大线程数, 创建线程来执行
			if(-1 == create_detach_pthread(pool, 1))
				return -1;
			pthread_cond_broadcast(&pool->cond);	// 广播创建好的线程来执行该任务(其他线程不会抢到这个任务)
		}
		// 线程数 = PTHREAD_MAX, 等待线程顺序执行任务
	}
	pthread_mutex_unlock(&pool->mutex);
	return 0;
}

/* 销毁线程池 */ 
int destory_pool(thread_pool_t *pool)
{
	tid_info_t *head = pool->tid_info_list;
	task_t *head2 = pool->task_list;
	// 删除tid_info_list及取消对应的线程
	while(head) {		
		pthread_cancel(head->tid);			// 取消线程
		if (head->next) 
			pool->tid_info_list = head->next;	// 从链表中删除
		else
			pool->tid_info_list = NULL;
		free(head);							// 释放空间
		head = pool->tid_info_list;
	}
	// 删除task_list
	while(head2) {		
		if (head2->next) 
			pool->task_list = head2->next;	// 从链表中删除
		else
			pool->task_list = NULL;
		free(head2);							// 释放空间
		head2 = pool->task_list;
	}
	pthread_mutex_destroy(&pool->mutex);
	pthread_cond_destroy(&pool->cond);
 	return 0;
}


测试程序: test.c

#include "my_pthread_pool.h"

void *do_task(void *arg)
{
	printf("%s\n", (char *)arg);
	sleep(1);
}
int main(int argc, char **argv)
{
	thread_pool_t pool;
	init_pool(&pool, 5);
	while(getchar()) {
		add_task(&pool,do_task, "do_task");	// "do_task"保存在全局数据段
	}
	
	//printf("n_cur_task = %d\n", pool.n_cur_task);
	destory_pool(&pool);
	return 0;
}

(5)  执行结果及分析


/* */

book@gui_hua_shu:~/test$ ./a.out
	
n_wait_thread = 1			// 初始创建了5个线程
n_wait_thread = 2
n_wait_thread = 3
n_wait_thread = 4
n_wait_thread = 5
	
add_task: n_cur_task = 1	// 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 4			// 占用1个线程
task_list:    = 0
npthread:     = 5
do_task		// sleep(1)

add_task: n_cur_task = 1	// 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 3			// 占用2个线程
task_list:    = 0
npthread:     = 5
do_task		

add_task: n_cur_task = 1	// 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 2			// 占用3个线程
task_list:    = 0
npthread:     = 5
do_task		
	
add_task: n_cur_task = 1	// 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 1			// 占用4个线程
task_list:    = 0
npthread:     = 5
do_task		
	// 占用5个线程
add_task: n_cur_task = 1	// 添加了1个任务, 链表中没有任务待处理, 线程数:5个
n_wait_thread = 0			// 占用5个线程
task_list:    = 0
npthread:     = 5
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:6个
task_list:    = 0
npthread:     = 6
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:7个
task_list:    = 0
npthread:     = 7
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:8个
task_list:    = 0
npthread:     = 8
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:9个
task_list:    = 0
npthread:     = 9
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:10个
task_list:    = 0
npthread:     = 10
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:11个
task_list:    = 0
npthread:     = 11
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:12个
task_list:    = 0
npthread:     = 12
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:13个
task_list:    = 0
npthread:     = 13
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:14个
task_list:    = 0
npthread:     = 14
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:15个
task_list:    = 0
npthread:     = 15
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:16个
task_list:    = 0
npthread:     = 16
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:17个
task_list:    = 0
npthread:     = 17
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:18个
task_list:    = 0
npthread:     = 18
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:19个
task_list:    = 0
npthread:     = 19
do_task
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 创建1个线程来执行,线程数:20个
task_list:    = 0
npthread:     = 20
do_task
	// 创建线程达到了上线20个, 让任务排队等待执行, 不再创建新线程来执行
add_task: n_cur_task = 1		// 添加了1个任务, 链表中还有1个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 2		// 添加了1个任务, 链表中还有2个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 3		// 添加了1个任务, 链表中还有3个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 4		// 添加了1个任务, 链表中还有4个任务待处理, 线程数为最大值:20个
add_task: n_cur_task = 5		// 添加了1个任务, 链表中还有5个任务待处理, 线程数为最大值:20个
task_list:    = 5				// 处理了1个任务, 链表中还有5个任务待处理, 线程数为最大值:20个
npthread:     = 20
do_task
task_list:    = 4				// 处理了一个任务, 链表中还有4个任务待处理, 线程数为最大值:20个
npthread:     = 20
do_task
task_list:    = 3				// 处理了一个任务, 链表中还有3个任务待处理, 线程数为最大值:20个
npthread:     = 20
do_task
task_list:    = 2				// 处理了一个任务, 链表中还有2个任务待处理, 线程数为最大值:20个
npthread:     = 20
do_task
task_list:    = 1				// 处理了一个任务, 链表中还有1个任务待处理, 线程数为最大值:20个
npthread:     = 20
do_task
task_list:    = 0				// 处理了一个任务, 链表中没有任务待处理,但是所有线程都被占用, 线程数为最大值:20个
npthread:     = 20
do_task

cancel: n_cur_thread = 19		// 第20个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 18		// 第19个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 17		// 第18个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 16		// 第17个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 15		// 第16个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 14		// 第15个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 13		// 第14个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 12		// 第13个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 11		// 第12个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 10		// 第11个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 9		// 第10个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 8		// 第9个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 7		// 第8个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 6		// 第7个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
cancel: n_cur_thread = 5		// 第6个线程执行完了, 任务链表没有待执行任务, 所以关闭额外创建的线程
// 初始创建的线程不关闭
n_wait_thread = 1				//第5个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 2				//第4个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 3				//第3个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 4				//第2个线程执行完了, 任务链表没有待执行任务, 该线程休眠
n_wait_thread = 5				//第1个线程执行完了, 任务链表没有待执行任务, 该线程休眠

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值