多线程之线程池实现---加优化

  1. 线程概念
    线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    同一进程的多个线程共享同一地址空间,因此,代码段,数据段都是共享的,只有栈是私有的.

  2. 相关头文件

    1. #include <pthread.h>
      在Linux下封装好的线程函数,通过 g++ ***.cpp -lpthread -o xxx 编译,./xxx 运行

      win下需要安装相关文件,可自行网上搜索。

    2. #include <unistd.h>
      Unix下开发的标准头文件,相当于windows.h

    3. #include <errno.h>
      用于错误信息

    4. #include <signal.h>
      pthread_kill函数的头文件,信号相关的函数

  3. 头文件对应的函数
    *一些注意事项
    *int 返回值默认是0为正确,其他错误或特殊含义(会强调)
    *当不为0时打印错误信息需要使用strerror(int ret)将错误信息转成字符串打印
    *线程不能调用exit或_exit函数,否则主线程也会结束,但可以调用pthread_exit结束自己
    *主线程调用pthread_exit会成为僵尸线程,等待子线程结束后结束

    线程:

    1. int pthread_create(pthread_t * thread,const pthread_arrt_t * arrt,void *(*function)(void *),void *arg);
      pthread_t thread: Linux 下为unsigned int,引用,返回线程的id号
      pthread_arrt_t *arrt: 线程属性,NULL为默认值
      void *(*function)(void *): 新线程将执行的函数对应的函数指针
      void *arg: 传入执行函数的唯一参数

      调用本函数将创建一个新的线程,线程将执行函数function,并将线程的id返回。需注意本函数调用的时候非同步,如果需要同步,请在函数中加条件休眠。线程池不需要。

    2. pthread_t pthread_self (void);
      返回线程的id号,可能会和create_thread的第一个参数不一样(当创建线程和填写id 号之间被中断时候)目前没有遇到过,就认为创建函数是原子操作吧。虽然是个隐患,但一直找不到很好解决策略,能让在主线程获取子线程id。

    3. void pthread_exit (void *retval);
      线程退出时候,void *retval返回线程传递出的参数,可以是值或者地址,但不能是线程的局部地址。线程池用不到返回值,因为线程要一直运行执行很多任务,此时可以设为NULL。

    4. int pthread_cancel (pthread_t thread);
      退出id为thread的线程,成功返回值为-1,即((void *) -1);

    5. int pthread_join (pthread_t thread,void **retval);
      pthread_t: 要回收的线程的id号
      void **vetval: 退出线程的返回值
      当调用此函数时,当前线程挂起,直到线程号为thread的线程终止。一种使用方法是声明一个void *tret; 每次调用函数的时候传递的参数是线程id号和&tret,当join函数返回时候,也就是当前线程结束挂起状态,线程号为thread的线程终止并返回(void *) 1,则在32位系统中用(int)tret即可得到返回的1(在64位中用(long)tret)。

      1. 当线程通过return返回,*retval存放thread线程函数的返回值void *类型
      2. 当线程通过pthread_cancel异常终止,*retval存放常量PTHREAD_CANCELD,即((void *) -1)
      3. 当线程通过自己调用pthread_exit终止,*retval存放传给pthread_exit的参数
    6. int pthread_detach (pthread_t thread);
      thread: 要被分离的线程id号
      调用后,线程的返回值不会保留到pthread_join函数调用,而是直接回收资源,在调用pthread_join会发生错误。用于不关心返回值的线程收尾。


    信号:

    1. pthread_kill(pthread_t thread,int signal);
      这里signal取0,用于判断id号为thread的线程是否存活。

    锁:
    *下面不是函数原型,是函数调用的格式
    *条件唤醒必须要在之前抢到锁,因为函数执行是解锁—等待—加锁

    1. int pthread_mutex_init (&pthread_mutex_t,NULL);
      初始化互斥锁pthread_mutex_t,正常返回0
    2. int pthread_cond_init (&pthread_cond_t,NULL);
      初始化条件pthread_cond_t,正常返回0
    3. pthread_mutex_destroy(&pthread_mutex_t);
      销毁互斥锁pthread_mutex_t
    4. pthread_cond_destroy(&pthread_cond_t);
      销毁条件pthread_cond_t
    5. pthread_mutex_lock (&pthread_mutex_t);
      抢互斥锁,保证不会出现两个线程同时访问一个数据,注意必须是同一个锁才可以。
    6. pthread_mutex_unlock (&pthread_mutex_t);
      释放互斥锁,让其他线程使用该数据。
    7. pthread_cond_wait (&pthread_cond_t,&pthread_mutex_t);
      释放互斥锁pthread_mutex_t,线程阻塞等待,直到接收到pthread_cond_t信号,开始唤醒,并抢互斥锁pthread_mutex_t,抢到后执行。
    8. pthread_cond_timedwait (&pthread_cond_t,&pthread_mutex_t,&timespec);
      释放互斥锁pthread_mutex_t,线程阻塞等待,直到接收到pthread_cond_t信号或者时间到,开始唤醒,并抢互斥锁pthread_mutex_t,抢到后执行。
      如果是因为接收到信号,返回值为0;超时返回值为110;错误为其他返回值。
    9. pthread_cond_signal (&pthread_cond_t);
      发送信号,使得一个等待被唤醒。
    10. pthread_cond_broadcast (&pthread_cond_t);
      发送信号,使得所有等待被唤醒,按抢到互斥锁先后轮流运行。
      **pthread_cond_timedwait的使用方法:
#define WAIT_BY_TIME(my_cond,my_lock,my_time,my_char) \
do {\
	struct timeval now;\
	struct timespec outtime;\
	gettimeofday(&now,NULL);\
	outtime.tv_sec = now.tv_sec + my_time;   /*最大等待时间*/\
	outtime.tv_nsec = now.tv_usec * 1000;\
	/*休眠到该可能要管理的时候*/\
	int ret = pthread_cond_timedwait(my_cond,my_lock,&outtime);\
	/*解锁---等待信号或时间---加锁*/\
	\
	if (ret == 0) {\
		printf(my_char " wake up by condition.\n");\
	} else if (ret == 110) {\
		printf(my_char " wake up by timeout.\n");\
	} else {\
		printf("----------- " my_char " wake up error------------\n");\
	}\
}while(0)
  1. 线程池
    1. 为什么要使用线程池?
      在高并发服务器上,往往需要使用多线程处理的方法来提高及时响应的能力,而线程池就是一个处理多线程很好的工具。

      在Unix网络编程中,线程与进程用于处理各项分支子功能,我们通常的操作是:接收消息 ==> 消息分类 ==> 线程创建 ==> 传递消息到子线程 ==> 线程分离 ==> 在子线程中执行任务 ==> 任务结束退出;

      这种处理方式在一些比较小型的局域网下下已经很足够了,但是在广域网中,很可能会发生服务器同时接收大量的服务请求。在这种情况下,创建与销毁线程都已经成为一种奢侈的开销,特别对于嵌入式服务器来说更应保证内存资源的合理利用;

      因此,线程池技术应运而生;线程池允许一个线程可以多次复用,且每次复用的线程内部的消息处理可以不相同,将创建与销毁的开销省去而不必来一个请求开一个线程;

    2. 线程池的组成
      线程池主要由管理者,任务队列和一些线程组成。管理者负责增删线程的数目,让线程数目和任务数目之间有一个动态平衡,任务队列存放尚未执行的任务,线程则负责运行这些任务。

    3. 线程池的代码实现方式
      首先初始化线程池,设置一些核心线程数,最大线程数,任务队列大小等等。将其保存在一个结构体中。并为线程id和任务队列存储创建空间。第一次初始化的时候先创建核心线程和管理者线程,所有的线程均一直运行到pthread_exit或者线程池关闭。

      工作线程检查任务队列,若为空则等待信号que_not_empty,若接受到信号,且函数抢到锁,则继续运行,判断是需要自己结束自己还是有新的任务来了,分别处理对应的情况。我这里的优化是若处理任务结束,相当于进入空闲状态,即线程数可能会有些多了,可以概率唤醒管理者。

      管理者线程每次等待别人的唤醒或者等到时间到达启动,检查线程数是否过多或者任务队列是否过多,删除或增加相应数目的线程。这里增加和删除数目加以优化,以尽可能少反复删除创建线程为目标。因此我采用预估于现实结合的方式,预估是判断任务队列中的任务将有多少在接下来的一段时间内(管理者删除线程前)被线程运行,现实就是当前忙碌和空闲的线程数目。删除线程数目也是一样计算,以此避免一个线程刚刚创建就被删除。

      任务添加函数在每次任务到达的时候进行判断,任务队列是否已满,线程池是否关闭,是否需要唤醒管理者等,最后发送信号,唤醒线程处理任务。

      线程池关闭函数则是先关闭管理者,在发送广播让所有存活的线程自杀,最后释放线程池的空间。

    4. 代码的优化

      1. 管理者部分
        1) 我的管理者启动不再依靠时间为唯一的标准,而是主要通过工作线程函数和任务添加函数发送信号来进行控制,这样可以让管理在更加合适的时间启动,节约资源。

        2)我的管理者增加和删除线程的算法加了优化,原来的代码是固定每次删除和增加线程的数目,靠宏定义来实现。而我的则是基于当前存活的线程数,当前忙碌的线程数,任务队列中的任务数动态的添加或删除不同数目的线程,保证线程数目波动相对稳定,减少刚刚添加或删除的线程又被删除或重新添加的情况。每次确保是确实需要修改线程数目的时候在进行修改。同时又默认线程大多数时候都会有许多任务要执行,因此每次尽可能多的添加线程。

      2. 工作线程部分
        1)增加了一个唤醒管理者的信号,在每次处理完成函数结束任务恢复空闲状态的时候,就有可能会存活的线程过多,需要删除若干个线程,但又不是每次忙完都需要删除,因为有可能线程又开始执行其他任务,而且只有忙碌线程数不足存活线程数一半时才需要调整,因此我设置一个1/3概率触发条件,启动管理者。

      3. 添加任务部分
        1)当添加任务后,任务队列的数目,比设置的最小等待数目的两倍还要多的时候,就意味这线程此时很可能处理不过来了,需要增加线程来满足如此多的任务,两倍的原因是如果设成单倍,很可能被线程运行了一个任务,管理者在看的时候任务数又少了,相当于本不需要唤醒管理者。这样就可以更快的调节线程数目,满足增加的任务。

      4. 全局优化
        1)一个很好的优化是map函数的使用。这主要是为了让线程可视化性更强,毕竟线程id值有些奇怪,原来的代码打印出来的都是一个类似地址的数字,很不好对比这看,而且这样修改还可以帮助我统计一共创建了多少个线程(当然要减一)。
        2)关于#define printf //printf,这条语句感觉加不加关系不是非常大,毕竟printf速度也还可以,而且多线程对于输出时间压缩还是不错的。也可能是因为输出比较少,感觉不到?
        3)#define WAIT_BY_TIME(my_cond,my_lock,my_time,my_char)这个宏定义简化了很多代码,而且学到了do{…}while(0) 没有分号的使用技巧,这样可以避免写代码的时候因为一条语句不加大括号产生的错误,而且也可以避免使用大括号时分号的问题。

    5. 代码的问题
      当然了,这个代码并不是完美的,依然存在这一些我还没有搞明白的问题,比如不管是原来的代码还是我自己写的代码,main函数有很大的限制,代码量如果稍稍加大,比如在main函数内在定义几个变量或者加上几条语句,再或者让指针p指向全局变量i,即int *p = &i; 都会发生段错误或free错误或指针错误。一直没有搞明白是为什么,如果有大佬们知道,欢迎在下面留言评论。

      对于效率方面,单纯看main函数执行时间上来说。本代码在一些情况下会好于网上的代码,比如当有2000-20000个任务同时到来(不休眠),每个任务有1s或者不休眠的情况下,本代码的算法要好于原来的代码,而对于任务数较少,如200个任务随机休眠后到来的情况,则相差不大,如果任务休眠时间过长或不休眠的时候还会慢于原来代码。

      这是因为一方面本代码考量的更多在同时大量任务的情况,创建尽可能多的线程,而任务少的时候创建释放会浪费过多资源,侧重点不同;另一方面,单纯看main函数其实并不代表代码真正的能力,因为main函数为了代码完整性加上了线程池释放部分的时间,而我们说线程池的创建初衷就是为了线程复用,如此频繁的开关线程池消耗是不言而喻的,也不是实际的情况。而本代码的缺陷恰恰在启动很多线程,关闭线程池时释放线程的时间损耗,因此在实际运用当中还是有很好的优势的。

      当然了,通过调整MIN_WAIT_TASK和增加线程数目的细节调整也可以使得算法时间消耗降低,本文就不再继续讨论这些情况了。不过可以肯定的是,在不考虑销毁线程池消耗的情况下,本代码还是有很大优势的,因为在动态任务到来的情况下,任务少时关闭线程并不会带来太多效率上的影响。实际测量也是如此。

废话不多说了,直接上代码了,毕竟注释也挺详细的。

源代码的参考链接:https://pan.baidu.com/s/1zWuoE3q0KT5TUjmPKTb1lw 密码:pp42

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <signal.h>
#include <map>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;

#define MIN_WAIT_TASK 1
#define ADMIN_WAIT_TIME 5
#define THREAD_WAIT_TIME 10
#define ADD_TASK_WAIT_TIME 20
//#define printf //printf

#define WAIT_BY_TIME(my_cond,my_lock,my_time,my_char) \
do {\
	struct timeval now;\
	struct timespec outtime;\
	gettimeofday(&now,NULL);\
	outtime.tv_sec = now.tv_sec + my_time;   /*最大等待时间*/\
	outtime.tv_nsec = now.tv_usec * 1000;\
	/*休眠到该可能要管理的时候*/\
	int ret = pthread_cond_timedwait(my_cond,my_lock,&outtime);\
	/*解锁---等待信号或时间---加锁*/\
	\
	if (ret == 0) {\
		printf(my_char " wake up by condition.\n");\
	} else if (ret == 110) {\
		printf(my_char " wake up by timeout.\n");\
	} else {\
		printf("----------- " my_char " wake up error------------\n");\
	}\
}while(0)

int globl_id = 1;
int globl_exit = 0;
map<unsigned long int,int > mp;

struct threadtask_t {                    //线程任务
	void *(*function)(void *);           //线程运行的函数指针
	void *arg;                           //函数的唯一参数
};

struct threadpool_t {                    //线程池
	pthread_mutex_t poollock;            //线程池锁
	pthread_mutex_t threadlock;          //线程锁
	pthread_cond_t que_not_empty;        //线程领取任务,若无则关掉线程
	pthread_cond_t que_not_full;         //任务队列不满,可以添加
	pthread_cond_t start_admin;          //用于唤醒admin线程
	
	pthread_t *thread;                   //线程数组,存放线程id;
	pthread_t admin_thread;              //管理线程
	threadtask_t *task;                  //任务数组,存放任务函数参数;
	
	//线程池数据
	int max_thr;                         //最大线程数
	int min_thr;                         //最小线程数
	int live_thr;                        //存活线程数
	int busy_thr;                        //工作线程数
	int exit_thr;                        //需要退出线程数
	int step;                            //一次增删线程数
	
	//任务队列数据
	int que_head;                        //队列头
	int que_rear;                        //队列尾
	int que_size;                        //队列元素个数
	int que_max_size;                    //队列最大大小
	
	int shutdown;                        //线程池状态,0为关闭
};

/*创建线程池*/
threadpool_t *create_threadpool(int min_thr_num, int max_thr_num, int queue_max_size,int step);
/*释放线程池*/
int threadpool_free(threadpool_t *pool);
/*销毁线程池*/
int threadpool_destroy(threadpool_t *pool);
/*管理线程*/
void *admin_thread(void *threadpool);
/*线程是否存在*/
bool is_thread_alive(pthread_t tid);
/*工作线程*/
void *threadpool_thread(void *threadpool);
/*向线程池的任务队列中添加一个任务*/
int threadpool_add_task(threadpool_t *pool, void *(*function)(void *arg), void *arg);

//创建线程池
threadpool_t * create_threadpool (int min_thr_num,int max_thr_num,int max_que_num,int step)
{
	//创建线程池模板
	threadpool_t *pool = (threadpool_t *)malloc(sizeof (threadpool_t));
	if (pool == NULL)
	{
		printf("cannot create thread pool\n");
		return NULL;
	}
	
	//初始化线程池
	pool->max_thr = max_thr_num;
	pool->min_thr = min_thr_num;
	pool->live_thr = min_thr_num;
	pool->busy_thr = 0;
	pool->exit_thr = 0;
	pool->step = step;
	pool->que_head = 0;
	pool->que_rear = 0;
	pool->que_size = 0;
	pool->que_max_size = max_que_num;
	pool->shutdown = 1;
	
	//初始化锁
	if (pthread_mutex_init(&(pool->poollock),NULL) != 0||
		pthread_mutex_init(&(pool->threadlock),NULL) != 0||
		pthread_cond_init(&(pool->que_not_empty),NULL) != 0||
		pthread_cond_init(&(pool->que_not_full),NULL) != 0)
	{
		printf("init lock or condition error\n");
		free(pool);
		return NULL;
	}
	
	//分配线程空间
	pool->thread = (pthread_t *)malloc(sizeof (pthread_t) * max_thr_num + 12);
	if (pool->thread == NULL)
	{
		printf("create thread error\n");
		free(pool);
		return NULL;
	}
	memset(pool->thread,0,sizeof (pthread_t) * max_thr_num + 12);
	
	//分配任务队列空间
	pool->task = (threadtask_t *)malloc(sizeof (threadtask_t) * max_que_num + 12);
	if (pool->task == NULL)
	{
		printf("create task error\n");
		free(pool);
		return NULL;
	}
	
	//启动最小线程数量
	for (int i = 0; i < min_thr_num; i++)
	{
		int ret = pthread_create(&(pool->thread[i]),NULL,threadpool_thread,(void *)pool);
		if (ret == 0) {
			mp.insert(map<unsigned long int,int >::value_type(pool->thread[i],globl_id++));
			printf("init thread %03d...\n",mp[pool->thread[i]]);
		} else {
			printf("init thread error: %s\n",strerror(ret));
		}
	}
	int ret = pthread_create(&(pool->admin_thread),NULL,admin_thread,(void *)pool);
	if (ret == 0) {
		printf("init admin 0x%x...\n",(unsigned int)pool->admin_thread);
	} else {
		printf("init admin error: %s\n",strerror(ret));
	}
	
	return pool;
}

/*释放线程池*/
int threadpool_free(threadpool_t *pool)
{
	if (pool == NULL)
	{
		return -1;
	}

	if (pool->task)
	{
		free(pool->task);
	}
	if (pool->thread)
	{
		free(pool->thread);
		pthread_mutex_lock(&(pool->poollock));               /*先锁住再销毁*/
		pthread_mutex_destroy(&(pool->poollock));
		pthread_mutex_lock(&(pool->threadlock));
		pthread_mutex_destroy(&(pool->threadlock));
		pthread_cond_destroy(&(pool->que_not_empty));
		pthread_cond_destroy(&(pool->que_not_full));
	}
	free(pool);
	pool = NULL;

	return 0;
}
 
/*销毁线程池*/
int threadpool_destroy(threadpool_t *pool)
{
	int i;
	if (pool == NULL)
	{
		return -1;
	}
	pool->shutdown = false;
	
	/*销毁管理者线程*/
	pthread_join(pool->admin_thread, NULL);
	
	//通知所有线程去自杀(在自己领任务的过程中)
	pthread_cond_broadcast(&(pool->que_not_empty));

	/*等待线程结束 先是pthread_exit 然后等待其结束*/
	for (i=0; i<pool->live_thr; i++)
	{
		pthread_join(pool->thread[i], NULL);
	}
   
	threadpool_free(pool);
	return 0;
}
 


//工作线程
void *threadpool_thread (void *arg)
{
	threadpool_t *pool = (threadpool_t *)arg;
	threadtask_t task;
	
	while(true)     //线程一直存活,直到自己结束或被cancel,节约反复创建资源
	{
		pthread_mutex_lock(&(pool->poollock));    //进入临界区,要读取线程池数据
		//当没有任务且线程池没有关闭的时候,一直阻塞即可
		while(pool->que_size == 0&&pool->shutdown == 1)
		{
			printf("%03d is waiting for server...\n",mp[pthread_self()]);
			
			WAIT_BY_TIME(&(pool->que_not_empty),&(pool->poollock),THREAD_WAIT_TIME,"thread");
			
			//唤醒!1.需要结束自己;2.有新的任务来了。情况2会退出while循环
			if (pool->exit_thr > 0)     //需要杀死线程
			{
				pool->exit_thr--;
				if (pool->live_thr > pool->min_thr)    //确实需要结束线程
				{
					printf("%03d is going to exit...\n",mp[pthread_self()]);
					pool->live_thr--;          //存活线程数减少
					pthread_mutex_unlock(&(pool->poollock));
					globl_exit++;
					pthread_exit(NULL);        //线程自己结束自己,不需要返回值
				}
			}
		}
		
		//线程池关闭情况
		if (pool->shutdown == 0)
		{
			printf("%03d is going to exit...\n",mp[pthread_self()]);
			pthread_mutex_unlock(&(pool->poollock));
			pthread_exit(NULL);        //线程自己结束自己,不需要返回值
		}
		
		//有任务到来
		printf("%03d is start working...\n",mp[pthread_self()]);
		//出队(此时依然是加锁状态)
		task.function = pool->task[pool->que_head].function;
		task.arg = pool->task[pool->que_head].arg;
		pool->que_size--;
		pool->que_head = (pool->que_head + 1) % pool->que_max_size;
		
		//广播可以添加新的任务了
		pthread_cond_broadcast(&(pool->que_not_full));
		
		//此时暂时不需要线程池的数据了
		pthread_mutex_unlock(&(pool->poollock));
		
		//对线程加锁,修改忙碌线程数目
		pthread_mutex_lock(&(pool->threadlock));
		pool->busy_thr++;
		pthread_mutex_unlock(&(pool->threadlock));
		
		//执行函数,如果需要返回值在这里自行取出!
		(*task.function)(arg);
		
		printf("%03d is end working...\n",mp[pthread_self()]);
		//对线程加锁,修改忙碌线程数目
		pthread_mutex_lock(&(pool->threadlock));
		pool->busy_thr--;
		if (rand() % 3 == 0)
		pthread_cond_signal(&(pool->start_admin));
		pthread_mutex_unlock(&(pool->threadlock));
	}
}

//管理线程
void *admin_thread(void *arg)
{
	threadpool_t *pool = (threadpool_t *)arg;
	printf("admin is running\n");
	
	while(pool->shutdown == 1)         //一直存活到线程池关闭
	{
		pthread_mutex_lock(&(pool->poollock));
		WAIT_BY_TIME(&(pool->start_admin),&(pool->poollock),ADMIN_WAIT_TIME,"admin");
		
		//寄存变量
		int size = pool->que_size;
		int live = pool->live_thr;
		pthread_mutex_unlock(&(pool->poollock));
		
		//寄存忙碌线程数目
		pthread_mutex_lock(&(pool->threadlock));
		int busy = pool->busy_thr;
		pthread_mutex_unlock(&(pool->threadlock));
		
		printf("busy_thr: %d; live_thr: %d; que_size: %d\n",busy,live,size);
		
		//变量是定值的不需要加锁哦!	
		//存活线程过多的情况	
		if (busy * 2 < live && live > pool->min_thr)
		{
			pthread_mutex_lock(&(pool->poollock));
			int exit = \
			pool->exit_thr = min(live - pool->min_thr, (live - busy * 2 - size) / 2 );
			pthread_mutex_unlock(&(pool->poollock));
			
			//一次性杀死step个线程
			for (int i = 0; i < exit; i++)
			{
				pthread_cond_signal(&(pool->que_not_empty));
			}
		}
		
		//任务队列处理不过来的情况
		if (size > MIN_WAIT_TASK && live < pool->max_thr)
		{
			pthread_mutex_lock(&(pool->poollock));
			//遍历线程数组,搜索尚未开启的线程,已经添加pool->step个线程后退出
			int step = pool->busy_thr * 2 + pool->que_size - pool->live_thr;
			printf("%d----------------------------------\n",step);
			for (int i = 0,add = 0; i < pool->max_thr && add < step; i++)
			{
				if (pool->live_thr == pool->max_thr)   //优化
				{
					break;
				}
				if (pool->thread[i] == 0 || !is_thread_alive(pool->thread[i]))  //可以添加线程
				{
					int ret = pthread_create(&(pool->thread[i]),NULL,threadpool_thread,(void *)pool);
					if (ret == 0) {
						mp.insert(map<unsigned long int,int >::value_type(pool->thread[i],globl_id++));
						printf("create thread %03d...\n",mp[pool->thread[i]]);
						add++;
						pool->live_thr++;
					} else {
						printf("create thread error: %s\n",strerror(ret));
					}
				}
			}	
			pthread_mutex_unlock(&(pool->poollock));
		}
	}
	return NULL;
}

//线程存活判断
bool is_thread_alive(pthread_t tid)
{
	int kill_rc = pthread_kill(tid,0);     //保留信号,返回线程是否存在
	if (kill_rc == ESRCH) {
		return false;
	} else {
		return true;
	}
}

//添加任务
int threadpool_add_task (threadpool_t *pool,void *(* function)(void *arg),void *arg)
{
	pthread_mutex_lock(&(pool->poollock));
	
	//线程池关闭
	if (pool->shutdown == 0)
	{
		printf("already close the thread pool!\n");
		pthread_mutex_unlock(&(pool->poollock));
		return -1;
	}
	
	//队列已满,启动管理者线程
	while (pool->que_size == pool->que_max_size)
	{
		printf("too many task!!!\n");
		pthread_cond_signal(&(pool->start_admin));
		WAIT_BY_TIME(&(pool->que_not_full),&(pool->poollock),ADD_TASK_WAIT_TIME,"add task1");
	}
	
	//清空队尾的返回值
	if (pool->task[pool->que_rear].arg != NULL)
	{
		free(pool->task[pool->que_rear].arg);
		pool->task[pool->que_rear].arg = NULL;
	}
	
	//添加任务
	pool->task[pool->que_rear].function = function;
	pool->task[pool->que_rear].arg = arg;
	pool->que_rear = (pool->que_rear + 1) % pool->que_max_size;
	pool->que_size ++;
	
	//任务有些多了
	if (pool->que_size > MIN_WAIT_TASK * 2)
	{
		printf("quit bit more task!!!\n");
		pthread_cond_signal(&(pool->start_admin));
		WAIT_BY_TIME(&(pool->que_not_full),&(pool->poollock),ADD_TASK_WAIT_TIME,"add task2");
	}
	
	//唤醒线程
	for (int i = 0; i < pool->que_size; i++)
	{
		pthread_cond_signal(&(pool->que_not_empty));
		//WAIT_BY_TIME(&(pool->que_not_full),&(pool->poollock),ADD_TASK_WAIT_TIME,"add task3");
	}
	
	pthread_mutex_unlock(&(pool->poollock));
}

//
int i;timeval t_start, t_end;
void* do_work(void *ptr)
{
    printf("hello %d\n",i);
    //sleep(3);
return NULL;
return ptr;
}

void f()
{
    gettimeofday( &t_end, NULL);
	cout << (t_end.tv_sec-t_start.tv_sec) + 
                    (t_end.tv_usec-t_start.tv_usec)/1000000.0 <<endl;
}

int main (void)
{
	 gettimeofday( &t_start, NULL);
    threadpool_t *thp = create_threadpool(10, 100, 100,0);
    printf("threadpool init ... ... \n");
    srand(time(0));

    void *p;
    for (i = 0;i < 2000; i ++)
    {
        threadpool_add_task(thp, do_work, (void *)p);
        //usleep(rand() % 100000);
    }
    printf("---------------------exit: %d id: %d--------------------\n",globl_exit,globl_id - 1);
    threadpool_destroy(thp);
    
    f();
    
    return 0;
}	

写在最后的话:如果你能有兴趣看完这些,那我真的很高兴。毕竟文字代码都很多,也没有什么图片,但确实是三四天的心血,还是希望可以完整的展现出来,希望可以有人认真读一读吧,当然回访的相关留言就不需要了,我也没有时间。怕什么真理无穷,进一寸有一寸欢喜。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值