首页 > 代码库 > 线程池实现

线程池实现

基于初级的线程池优化,这里最主要的问题是解决线程id的管理问题,以及线程取消的管理
这里采用链表来管理线程id,链表的特性便于新增和删除,引进thread_revoke结构体来标记全局的取消线程信息,先分析一下线程什么时候需要取消:当任务很少,少到tasknum <threadnum* THREAD_WORKER_LOW_RATIO 时,也就是,任务的增加速度远远赶不上线程的处理速度时,会导致一部分线程处于饥饿状态(阻塞)。那么我们需要取消的就是处于饥饿状态的线程。对于poisx多线程编程中线程的取消,一般采用pthread_cancel()而且此函数是无阻塞返回的(即不等线程是否真的取消)而且,这里有一个很大很大的疑惑,在线程取消的时候,线程必须等到在取消执行点处取消,这里我们的取消执行点是pthread_cond_wait()这线程处理阻塞状态,互斥锁在线程插入阻塞等待队列时已经释放掉。但是,线程本身处于阻塞状态下(放弃了时间片)会执行取消动作吗?我试验了一下,没有……
这里维护一个取消队列,在线程取消时,置全局取消标志位为1,pthread_broadcast()唤醒所有线程,让在线程唤醒时会判断是否进入取消状态,如果是直接主动退出。当然这里有一个取消计数。
在我写的线程池中,实现了对线程的自动管理,根据任务的多少,自动增加线程或者删除线程,来保持资源的利用率。
my_thread_pool.h
#ifndef _MY_THREADPOOL_HEAD_
#define _MY_THREADPOOL_HEAD_

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<sys/types.h>

#define THREAD_MINNUM 2
#define THREAD_MAXNUM 20
#define THREAD_DEFAULT 2

#define MANAGE_ADJUST_INTERVAL  5
#define WORKER_THREAD_HIGH_RATIO 3
#define WORKER_THREAD_LOW_RATIO 1

#define BUFFSIZE 1024

//task in pthread pool
typedef struct pthread_work           //任务信息
{
	void* (*task)(void *);
	void* args;                       //任务所带参数(就是pthread_work_info)
	struct pthread_work* next;
}pthread_work;

typedef struct pthread_work_info    //用于表示执行任务的信息
{
	pthread_t pid;                 //哪个线程正在执行该任务
	int number;                    //用来表示这是第几个任务
}pthread_work_info;

typedef struct pthread_info         //线程信息(用于线程池中存储阻塞线程)
{
	pthread_t pid;
	struct pthread_info* next;
}pthread_info;

typedef struct thread_pool
{
	int thread_currnum;    //线程池中线程的个数
	int thread_blocknum;   //线程池中阻塞的线程个数
	int thread_work;      // 线程池中阻塞的任务个数
	pthread_mutex_t pool_lock;
	pthread_cond_t pool_cond;

	pthread_work* work_head;     //阻塞任务的起始地址
	pthread_info* pthread_head;  //阻塞线程的头节点
	int shutdown;
	pthread_t manager_thread;    //监控线程
}thread_pool;

typedef struct pthread_invoke
{
	int flag;                   //是否要删除线程
	int invoked;               //已删除线程个数
	int need_invoke;           //需要删除线程个数
}pthread_invoke;

pthread_invoke *invoke=NULL;
thread_pool *pool=NULL;

void init_pool();
void init_invoke();
void destroy_pool();
int add_work_pool(pthread_work* );
int add_pthread_pool();
int delete_thread_pool();
void* pthread_run(void *);
void* manager_pthreadpool(void *);
void* my_process(void *args);

#endif


my_thread_pool.c实现
#include"my_thread_pool.h"
#include<fcntl.h>
#include<string.h>

void init_pool()
{
    //初始化线程池,创建默认个数的阻塞线程
	pool=(thread_pool *)malloc(sizeof(thread_pool));
	pthread_mutex_init(&pool->pool_lock,NULL);
	pthread_cond_init(&pool->pool_cond,NULL);
	pool->shutdown=0;
	pool->work_head=NULL;
	pool->thread_currnum=THREAD_DEFAULT;
	pool->thread_blocknum=THREAD_DEFAULT;
	pool->thread_work=0;
	int i=0;
	pthread_info *p,*q;
	for(i=0;i<THREAD_DEFAULT;i++)
	{
		if(pool->pthread_head==NULL)
		{
			pool->pthread_head=(pthread_info*)malloc(sizeof(pthread_info));
			pool->pthread_head->next=NULL;
			p=pool->pthread_head;
		}
		else if(i==THREAD_DEFAULT-1)
		{
			q=(pthread_info*)malloc(sizeof(pthread_info));
			q->next=NULL;
			p->next=q;
		}
		else
		{
			q=(pthread_info*)malloc(sizeof(pthread_info));
			q->next=NULL;
			p->next=q;
			p=q;
		}
	}
	p=pool->pthread_head;

	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	for(i=0;i<THREAD_DEFAULT;i++)
	{
		pthread_create(&p->pid,NULL,pthread_run,(void*)0);
		p=p->next;
	}
	//manager thread number
	pthread_create(&pool->manager_thread,NULL,manager_pthreadpool,NULL);
}

void* pthread_run(void *args)
{
	int flag=0;
	//创建线程有2种方式,刚开始创建的都是阻塞线程。当线程数量不够时,再增加的线程是都不是阻塞线程,可直接执行的
	int flag_block=(int)args;   //flag_block=0 represent it is blocked
	pthread_t pthread_id=pthread_self();
	printf("thread %x start \n",pthread_id);
	while(1)
	{
		pthread_mutex_lock(&pool->pool_lock);
		//printf("thread %x start \n",pthread_id);
		while(flag_block==0&&pool->shutdown==0&&pool->work_head==NULL)
		{
			if(flag)
				printf("thread %x will waiting\n",pthread_id);
			pthread_cond_wait(&pool->pool_cond,&pool->pool_lock);
		}
		if(pool->shutdown==1)
		{
			printf("thread %x will exit\n",pthread_id);
			pthread_mutex_unlock(&pool->pool_lock);
			pthread_exit(NULL);
		}
		pthread_info* currnode=pool->pthread_head;
		pthread_info* prexnode=pool->pthread_head;
		// remove the thread from the block queue
		while(currnode!=NULL)
		{
			if(currnode->pid==pthread_id)
				break;
			prexnode=currnode;
			currnode=currnode->next;
		}
		if(currnode!=NULL)
		{
			if(currnode==pool->pthread_head)
			{
				pool->pthread_head=pool->pthread_head->next;
				free(prexnode);
			}
			else if(currnode->next==NULL)
			{
				prexnode->next=NULL;
				free(currnode);
			}
			else
			{
				prexnode->next=currnode->next;
				free(currnode);
			}
		}
		if(invoke->flag==1&&invoke->invoked<invoke->need_invoke)
		{
			printf("the pthread %x is useless ,will exit\n",pthread_self());
			//delete ptthread_id from queue
			invoke->invoked++;
			pool->thread_currnum--;
			pool->thread_blocknum--;
			pthread_mutex_unlock(&pool->pool_lock);
			pthread_exit(NULL);
		}
		assert(pool->work_head!=NULL);
		pthread_work* work=pool->work_head;
		pool->work_head=pool->work_head->next;
		((pthread_work_info*)work->args)->pid=pthread_id;
		pool->thread_work--;
		if(flag_block==0)
			pool->thread_blocknum--;
		pthread_mutex_unlock(&pool->pool_lock);
		(*work->task)(work->args);
		flag=1;
		pthread_mutex_lock(&pool->pool_lock);
		//if there is no work ,then the thread will block ,put it into blockqueue
		if(pool->work_head==NULL)
		{
			flag_block=0;
			pthread_info* p=(pthread_info *)malloc(sizeof(pthread_info));
			p->next=pool->pthread_head;
			p->pid=pthread_self();
			pool->pthread_head=p;
			//input into blocknum ,then should add thread_blocknum
			pool->thread_blocknum++;
		}
		else  //执行任务后,还有任务,故不阻塞线程
			flag_block=1;
		pthread_mutex_unlock(&pool->pool_lock);
	}
	pthread_exit(NULL);
}

void destroy_pool()  //释放资源
{
	if(pool==NULL)
		return ;
	pthread_mutex_lock(&pool->pool_lock);
	pool->shutdown=1;
	pthread_mutex_unlock(&pool->pool_lock);
	pthread_cond_broadcast(&pool->pool_cond);
	//wait all pthread free rescource
	sleep(5);

	//free rescource
	pthread_cond_destroy(&pool->pool_cond);
	pthread_mutex_destroy(&pool->pool_lock);
	pthread_work* p=pool->work_head;
	while(p!=NULL)
	{
		pool->work_head=pool->work_head->next;
		free(p);
		p=pool->work_head;
	}
	pthread_info* info=pool->pthread_head;
	while(info!=NULL)
	{
		pool->pthread_head=pool->pthread_head->next;
		free(info);
		info=pool->pthread_head;
	}
	free(pool);
	pool=NULL;
	return ;
}

void *my_process(void* args)
{
	pthread_work_info *work_info =(pthread_work_info *)args;
	printf("%x thread is working task %d\n",work_info->pid,work_info->number);
	sleep(1);
	return ;
}

int add_work_pool(pthread_work* work)
{
	pthread_mutex_lock(&pool->pool_lock);
	pthread_work* p=pool->work_head;
	if(p==NULL)
		pool->work_head=work;
	else
	{
		while(p->next!=NULL)
			p=p->next;
		p->next=work;
	}
	pool->thread_work++;
	pthread_mutex_unlock(&pool->pool_lock);
	pthread_cond_signal(&pool->pool_cond);
	return 1;

}

int main()
{
	init_pool();
	init_invoke();
	int i;
	pthread_work * p;

	for(i=0;i<40;i++)
	{
		if(i>=20)
			sleep(1);
		p=(pthread_work *)malloc(sizeof(pthread_work));
		p->task=my_process;
		p->next=NULL;
		p->args=malloc(sizeof(pthread_work_info));
		((pthread_work_info *)p->args)->number=i;
		add_work_pool(p);
		//sleep(1);
	}
	sleep(15);  //等待所有任务都执行完,然后在删除线程池
	destroy_pool();
	return 0;
}

//use for manager pthreadpool every 5 seconds
void* manager_pthreadpool(void *args)
{
	char buff[BUFFSIZE];
	while(1)
	{
		sleep(5);
		pthread_mutex_lock(&pool->pool_lock);
		if(pool->shutdown==1)
			pthread_exit(NULL);
		printf("the work num is %d,the thread num: %d , the ratio is %lf,the block is %d\n",pool->thread_work,pool->thread_currnum,(double)pool->thread_work/pool->thread_currnum,pool->thread_blocknum);
		//display all the block pid;
		pthread_info* p=pool->pthread_head;
		while(p!=NULL)
		{
			printf("%x  thread is blocked\n",p->pid);
			p=p->next;
		}

		if(pool->thread_work/pool->thread_currnum>WORKER_THREAD_HIGH_RATIO)
		{
			//you must add thread number to adjust to the work(beacuse work is too many)
			int add_number=(pool->thread_work-pool->thread_currnum*WORKER_THREAD_HIGH_RATIO)/WORKER_THREAD_HIGH_RATIO;
			int i,err;
			pthread_attr_t attr;
			err=pthread_attr_init(&attr);
			if(err!=0)
				printf("attr error\n");
			err=pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
			for(i=1;i<=add_number;i++)
			{
                //这些注释掉的语句是,因为再次创建的线程都不会是阻塞线程,所有不用放入队列
				//pthread_info *p=(pthread_info *)malloc(sizeof(pthread_info));
				//p->next=pool->pthread_head;
				//p->pid=(pthread_t)0;
				pthread_t pid;
				pthread_create(&pid,&attr,pthread_run,(void *)1);
				//pool->pthread_head=p;
				pool->thread_currnum++;
				printf("thread %x add into the pool\n",pid);
			}
			pthread_mutex_unlock(&pool->pool_lock);
			//strcpy(buff,"you must add thread\n");
			//int size=strlen(buff);
			//write_data("a.txt",buff,size);

		}
		else if(pool->thread_work/pool->thread_currnum<WORKER_THREAD_LOW_RATIO)
		{
			//you must decrease thread number to adjust to the work
			if(pool->thread_blocknum!=0&&pool->thread_currnum>THREAD_MINNUM)
			{
				invoke->flag=1;
				invoke->invoked=0;
				invoke->need_invoke=(pool->thread_currnum-pool->thread_blocknum>=THREAD_MINNUM)?pool->thread_blocknum:(pool->thread_currnum-THREAD_MINNUM);
				pthread_mutex_unlock(&pool->pool_lock);
				pthread_cond_broadcast(&pool->pool_cond);
			}
			pthread_mutex_unlock(&pool->pool_lock);

		}
		else
			pthread_mutex_unlock(&pool->pool_lock);

	}
}
//初始化删除线程标志
void init_invoke()
{
	invoke=(pthread_invoke*)malloc(sizeof(pthread_invoke));
	invoke->flag=0;
	invoke->invoked=0;
	invoke->need_invoke=0;
}