Linux下线程池

   1.获取当前线程的ID号
             pthread_t pthread_self(void);
                    返回值:就是线程的ID号

线程池
=====================================
   1.线程池需要封装的函数(核心函数只有三个)
       (1)线程池的初始化
       (2)添加任务
       (3)线程池的销毁
  

 使用万能函数指针表示不用的任务

#include "myhead.h"
//多个参数--》变成一个参数--》用结构体就ok了
struct argument
{
	int n1;
	int n2;
};

//三个不同的任务
int add(int a,int b)
{
	return a+b;
}

int showbmp(char *bmppath)
{
	printf("我正在帮你显示%s图片!\n",bmppath);
	return 0;
}

void shownum(int *buf,int n)
{
	int i;
	for(i=0; i<n; i++)
		printf("数组元素是:%d\n",buf[i]);
	
}

//把刚才的三个任务进行改造,让它们跟万能函数指针相匹配
void *newadd(void *arg)  //add有两个参数,并且不是指针,如何变成一个参数,而且是指针呢
{
	int *p=malloc(sizeof(int));
	struct argument *q=(struct argument *)arg;
	*p=(q->n1)+(q->n2);
	return p;
}



int main()
{
	struct argument arg={15,18};
	//定义万能函数指针
	void *(*p)(void *);
	p=newadd;
	
	//通过函数指针调用函数
	int *ret=p(&arg);  //等价于newadd(&arg)
	printf("结果是:%d\n",*ret);
}

线程池三大基本函数的实现

#include "myhead.h"

//多个参数--》变成一个参数--》用结构体就ok了
struct argument
{
	int n1;
	int n2;
};

struct newargument
{
	int n;
	int buf[10];
};

void *newadd(void *arg)  //add有两个参数,并且不是指针,如何变成一个参数,而且是指针呢
{
	printf("我是任务一!\n");
	int *p=malloc(sizeof(int));
	struct argument *q=(struct argument *)arg;
	*p=(q->n1)+(q->n2);
	return p;
}

void *newshowbmp(void *arg)
{
	printf("我是任务二,我正在帮你显示%s图片!\n",(char *)arg);
	return NULL;
}

void *newshownum(void *arg)
{
	struct newargument *p=(struct newargument *)arg;
	int i;
	for(i=0; i<p->n; i++)
		printf("我是任务三数组元素是:%d\n",p->buf[i]);
	return NULL;
}

//定义结构体表示你的任务链表
struct tasklist
{
	//数据域
	void *(*taskp)(void *);  //函数指针,用来指向你要存放的任务
	void *taskarg;           //传递给taskp的参数
	//指针域
	struct tasklist *next;
};

//全局变量,存放任务链表的表头
struct tasklist *myhead;

//定义结构体表示线程池
struct threadpool
{
	int threadnum; //线程数量
	pthread_t *threadid; //保存线程ID
	int tasknum; //统计任务数量
	struct tasklist *taskhead; //任务链表头结点
	pthread_mutex_t threadmutex;  //互斥锁
	pthread_cond_t threadcond;   //条件变量(当任务处理完毕,用来阻塞线程)
};

//初始化链表的头结点
struct tasklist *task_init()
{
	struct tasklist *head=malloc(sizeof(struct tasklist));
	head->next=NULL;
	return head;
}

//线程池中多个线程共用同一个任务函数
//任务函数需要完成的功能:不断地从任务链表中取出任务,然后去执行任务
//                        如果任务链表中任务数量为0,线程需要阻塞,等到有新的任务存放到链表中,就解除阻塞
void *routine(void *arg)
{
	struct threadpool *temp=(struct threadpool *)arg;
	printf("线程池中的某个线程已经开始工作了,它的ID号是:%lu\n",pthread_self());
	while(1)
	{
		//上锁
		pthread_mutex_lock(&(temp->threadmutex));
		
		//判断如果任务数量为0,应该要阻塞当前线程(道理跟卖票的例子一模一样)
		while(temp->tasknum<=0)
		{
			printf("线程阻塞在wait这里了,原因是现在任务链表中还没有添加任务!\n");
			//阻塞你的线程
			pthread_cond_wait(&(temp->threadcond),&(temp->threadmutex));
		}
		
		struct tasklist *p=myhead->next;
		//从任务链表头结点的下一个位置取出任务(说白了就是删除头结点后面的那个结点),然后执行该任务
		myhead->next=p->next;
		p->next=NULL;
		//更新任务数量
		temp->tasknum--;
		//解锁
		pthread_mutex_unlock(&(temp->threadmutex));
		//执行p指向的那个结点中存放的任务
		(p->taskp)(p->taskarg);
		free(p);
		
	}
}

//封装线程池的初始化
struct threadpool *pool_init(int num)
{
	int i;
	struct threadpool *pool=malloc(sizeof(struct threadpool));
	pool->threadnum=num; //线程数量
	pool->threadid=malloc(num*sizeof(pthread_t)); //存放线程ID
	pool->tasknum=0;  //任务数量
	pool->taskhead=NULL;  //链表表头
	pthread_mutex_init(&(pool->threadmutex),NULL);  //初始化锁
	pthread_cond_init(&(pool->threadcond),NULL);    //初始化条件变量
	
	//循环创建num个线程
	for(i=0; i<num; i++)
		pthread_create(pool->threadid+i,NULL,routine,pool);
	return pool;
}

//尾插任务到单链表中
//把void *(*p)(void *)指向的函数(任务)插入到head代表的链表尾部
int add_task(void *(*p)(void *),void *arg,struct threadpool *pool)
{
	//准备新的结点
	struct tasklist *newnode=malloc(sizeof(struct tasklist));
	newnode->taskp=p;
	newnode->taskarg=arg;
	newnode->next=NULL;
	
	//上锁
	pthread_mutex_lock(&(pool->threadmutex));
	//尾插到链表中
	struct tasklist *temp=myhead;
	while(temp->next!=NULL)
		temp=temp->next;
	temp->next=newnode;
	
	//更新任务数量
	pool->tasknum++;
	pthread_mutex_unlock(&(pool->threadmutex));
	
	//唤醒某个线程,让线程去执行你刚才添加的任务
	pthread_cond_signal(&(pool->threadcond));
	return 0;
}


int main()
{
	struct argument arg1={78,12};
	struct newargument arg2;
	arg2.n=4;
	arg2.buf[0]=74;
	arg2.buf[1]=84;
	arg2.buf[2]=94;
	arg2.buf[3]=104;
	
	//初始化线程池--》线程池负责从任务链表中取出任务然后处理
	struct threadpool *mypool=pool_init(6);  //我想创建6个线程
	
	//初始化任务链表的头结点
	myhead=task_init();
	mypool->taskhead=myhead;
	
	//主函数负责添加任务到任务链表中
	//把刚才的三个任务尾插到单链表中
	add_task(newadd,&arg1,mypool);
	add_task(newshowbmp,"/home/gec/1.bmp",mypool);
	add_task(newshownum,&arg2,mypool);
	
	
	while(1); //防止主函数退出,导致程序奔溃
}

线程池三大函数实现终极版

#include "myhead.h"

//多个参数--》变成一个参数--》用结构体就ok了
struct argument
{
	int n1;
	int n2;
};

struct newargument
{
	int n;
	int buf[10];
};

void *newadd(void *arg)  //add有两个参数,并且不是指针,如何变成一个参数,而且是指针呢
{
	printf("我是任务一!\n");
	int *p=malloc(sizeof(int));
	struct argument *q=(struct argument *)arg;
	*p=(q->n1)+(q->n2);
	return p;
}

void *newshowbmp(void *arg)
{
	printf("我是任务二,我正在帮你显示%s图片!\n",(char *)arg);
	return NULL;
}

void *newshownum(void *arg)
{
	struct newargument *p=(struct newargument *)arg;
	int i;
	for(i=0; i<p->n; i++)
		printf("我是任务三数组元素是:%d\n",p->buf[i]);
	return NULL;
}

//定义结构体表示你的任务链表
struct tasklist
{
	//数据域
	void *(*taskp)(void *);  //函数指针,用来指向你要存放的任务
	void *taskarg;           //传递给taskp的参数
	//指针域
	struct tasklist *next;
};

//全局变量,存放任务链表的表头
struct tasklist *myhead;

//定义结构体表示线程池
struct threadpool
{
	int threadnum; //线程数量
	pthread_t *threadid; //保存线程ID
	int tasknum; //统计任务数量
	struct tasklist *taskhead; //任务链表头结点
	pthread_mutex_t threadmutex;  //互斥锁
	pthread_cond_t threadcond;   //条件变量(当任务处理完毕,用来阻塞线程)
	bool shutdown;   //标记线程池是否关闭
};

//初始化链表的头结点
struct tasklist *task_init()
{
	struct tasklist *head=malloc(sizeof(struct tasklist));
	head->next=NULL;
	return head;
}

//线程池中多个线程共用同一个任务函数
//任务函数需要完成的功能:不断地从任务链表中取出任务,然后去执行任务
//                        如果任务链表中任务数量为0,线程需要阻塞,等到有新的任务存放到链表中,就解除阻塞
void *routine(void *arg)
{
	struct threadpool *temp=(struct threadpool *)arg;
	//printf("线程池中的某个线程已经开始工作了,它的ID号是:%lu\n",pthread_self());
	while(1)
	{
		//上锁
		pthread_mutex_lock(&(temp->threadmutex));
		
		//判断如果任务数量为0,应该要阻塞当前线程(道理跟卖票的例子一模一样)
		while(temp->tasknum<=0 && temp->shutdown==false)
		{
			//printf("线程阻塞在wait这里了,原因是现在任务链表中还没有添加任务!\n");
			//阻塞你的线程
			pthread_cond_wait(&(temp->threadcond),&(temp->threadmutex));
		}
		if(temp->tasknum<=0 && temp->shutdown==true)
		{
			//解锁--》防止死锁
			pthread_mutex_unlock(&(temp->threadmutex));
			printf("线程池中某个线程退出了,它的ID是:%lu!\n",pthread_self());
			//结束线程
			pthread_exit(NULL);
		}
		
		
		struct tasklist *p=myhead->next;
		//从任务链表头结点的下一个位置取出任务(说白了就是删除头结点后面的那个结点),然后执行该任务
		myhead->next=p->next;
		p->next=NULL;
		//更新任务数量
		temp->tasknum--;
		//解锁
		pthread_mutex_unlock(&(temp->threadmutex));
		//执行p指向的那个结点中存放的任务
		(p->taskp)(p->taskarg);
		free(p);
		
	}
}

//封装线程池的初始化
struct threadpool *pool_init(int num)
{
	int i;
	struct threadpool *pool=malloc(sizeof(struct threadpool));
	pool->threadnum=num; //线程数量
	pool->threadid=malloc(num*sizeof(pthread_t)); //存放线程ID
	pool->tasknum=0;  //任务数量
	pool->taskhead=NULL;  //链表表头
	pthread_mutex_init(&(pool->threadmutex),NULL);  //初始化锁
	pthread_cond_init(&(pool->threadcond),NULL);    //初始化条件变量
	pool->shutdown=false;  //表示线程池没有关闭
	
	//循环创建num个线程
	for(i=0; i<num; i++)
		pthread_create(pool->threadid+i,NULL,routine,pool);
	return pool;
}

//尾插任务到单链表中
//把void *(*p)(void *)指向的函数(任务)插入到myhead代表的链表尾部
int add_task(void *(*p)(void *),void *arg,struct threadpool *pool)
{
	//准备新的结点
	struct tasklist *newnode=malloc(sizeof(struct tasklist));
	newnode->taskp=p;
	newnode->taskarg=arg;
	newnode->next=NULL;
	
	//上锁
	pthread_mutex_lock(&(pool->threadmutex));
	//尾插到链表中
	struct tasklist *temp=myhead;
	while(temp->next!=NULL)
		temp=temp->next;
	temp->next=newnode;
	
	//更新任务数量
	pool->tasknum++;
	pthread_mutex_unlock(&(pool->threadmutex));
	
	//唤醒某个线程,让线程去执行你刚才添加的任务
	pthread_cond_signal(&(pool->threadcond));
	return 0;
}


//封装线程池的销毁
int pool_destroy(struct threadpool *pool)
{
	int i;
	//改变标志位的值,让线程具备退出条件
	pool->shutdown=true;
	//唤醒所有的线程
	pthread_cond_broadcast(&(pool->threadcond));
	for(i=0; i<pool->threadnum; i++)
		pthread_join(*(pool->threadid+i),NULL);
	//释放之前申请的堆空间
	free(pool->threadid);
	free(myhead);
	free(pool);
	return 0;
}


int main()
{
	struct argument arg1={78,12};
	struct newargument arg2;
	arg2.n=4;
	arg2.buf[0]=74;
	arg2.buf[1]=84;
	arg2.buf[2]=94;
	arg2.buf[3]=104;
	
	//初始化线程池--》线程池负责从任务链表中取出任务然后处理
	struct threadpool *mypool=pool_init(6);  //我想创建6个线程
	
	//初始化任务链表的头结点
	myhead=task_init();
	mypool->taskhead=myhead;
	
	//主函数负责添加任务到任务链表中
	//把刚才的三个任务尾插到单链表中
	add_task(newadd,&arg1,mypool);
	add_task(newshowbmp,"/home/gec/1.bmp",mypool);
	add_task(newshownum,&arg2,mypool);
	
	//封装线程池的销毁
	pool_destroy(mypool);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hqb_newfarmer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值