背景
理发店有一间工作室,有一名理发师,接待室内有n(n≥1)把椅子。如果没有顾客,理发师就去睡觉;如果理发师在睡觉;有顾客需要理发,则顾客会唤醒他;如果理发师在忙且接待室有空闲椅子,那么此顾客会坐在其中1把空闲的椅子上等待;如果来时所有椅子都有人,那么顾客离去。
head.h
head中存放常用的头文件
#define _HEAD_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <sys/ioctl.h>
#include <string.h>
#include <math.h>
#include <sys/stat.h>
#include<time.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include <dirent.h>
#include<pthread.h>
#include"thread_pool.h"
#include "color.h"
#ifdef _D
#define DBG(fmt, args...) printf(fmt, ##args)//如果可变参数列表没有东西,那么##代表去掉,#代表保留,
#else
#define DBG(fmt, args...)
#endif
thread_pool.h
线程池的头文件,定义一个队列,队列中中有head,tail,size,total,mutex用于保护数据,cond用于接收信号量,data用来存放数据地址指针
typedef struct task_queue{
int head,tail,size,total;
void **data;
pthread_mutex_t mutex;//加锁
pthread_cond_t cond;//信号
}task_queue;
void task_queue_init(struct task_queue *taskQueue,int size);
void task_queue_push(struct task_queue *taskQueue,void *data);//将这个空间的东西压入队列中
void *task_queue_pop(struct task_queue *taskQueue);
//线程池
void *thread_run(void *arg);//线程处理函数
color.h
#define NONE "\033[m"
#define RED "\033[0;32;31m"
#define LIGHT_RED "\033[1;31m"
#define GREEN "\033[0;32;32m"
#define LIGHT_GREEN "\033[1;32m"
#define BLUE "\033[0;32;34m"
#define LIGHT_BLUE "\033[1;34m"
#define DARY_GRAY "\033[1;30m"
#define CYAN "\033[0;36m"
#define LIGHT_CYAN "\033[1;36m"
#define PURPLE "\033[0;35m"
#define LIGHT_PURPLE "\033[1;35m"
#define BROWN "\033[0;33m"
#define YELLOW "\033[1;33m"
#define LIGHT_GRAY "\033[0;37m"
#define WHITE "\033[1;37m"
head_pool.cc
任务队列初始化
任务队列中传入多少个任务,total,head,tail全部置0,开辟出size个,大小为void*的data,他的类型为void**所以要转成void**,最后再初始化一下互斥锁。
void task_queue_init(struct task_queue *taskQueue,int size){
taskQueue->size=size;//有多少数量
taskQueue->total=taskQueue->head=taskQueue->tail=0;
taskQueue->data=(void**)calloc(size,sizeof(void *));
pthread_mutex_init(&taskQueue->mutex,NULL);
return ;
}
push任务
进入先上锁,如果total==size则说明队列已满,直接返回,如果没满则在尾部插入data,再另total++和tail++,如果tail==size说明队列已经到了末尾的位置,那么就要回到数组头的位置重新开始,最后等待发送信号signal,wait在等待他发送的信号后解锁。
void task_queue_push(struct task_queue *taskQueue,void *data){
pthread_mutex_lock(&taskQueue->mutex);//共享区,只能允许一个人进去
if(taskQueue->total==taskQueue->size){
DBG(YELLOW"<push> : taskQueue is full\n"NONE);
pthread_mutex_unlock(&taskQueue->mutex);
return ;
}
taskQueue->data[taskQueue->tail]=data;
DBG(GREEN"<push> : "RED"data"NONE"is pushed!\n");
taskQueue->tail++;
taskQueue->total++;
if(taskQueue->tail==taskQueue->size){
DBG(YELLOW"<push> taskQueue reach tail!\n"NONE);
taskQueue->tail=0;
}
pthread_cond_signal(&taskQueue->cond);//等发信号
pthread_mutex_unlock(&taskQueue->mutex);//解锁
return;
}
pop任务
进行操作先上锁,如果total==0,说明当前没有任务,那么只需要等着发信号就行。
data在数据的头取出,取出后total--,head++,如果head==size,那么则说明head到达了最后一个,就将head指向0号位置,最后解锁,返回给线程池中data数据。
void* task_queue_pop(struct task_queue *taskQueue){
pthread_mutex_lock(&taskQueue->mutex);
while(taskQueue->total==0){//惊群效应
//拿不到等着就行了,不要用轮询(非阻塞)
//手册写自动解锁,等待条件变量被发信号,线程执行会被挂起,不占cpu知道条件变量被发信号, 要在入口处被锁住,在返回前会重新锁上
pthread_cond_wait(&taskQueue->cond,&taskQueue->mutex);
}
void *data=taskQueue->data[taskQueue->head];
DBG(GREEN"<pop> :"BLUE"data"NONE"is poped!\n");
taskQueue->total--;
taskQueue->head++;
if(taskQueue->head==taskQueue->size){
DBG(YELLOW"<pop> taskQueue reach tail!\n"NONE);
taskQueue->head=0;
}
pthread_mutex_unlock(&taskQueue->mutex);
return data;
}
总代码
#include"head.h"
#include"color.h"
void task_queue_init(struct task_queue *taskQueue,int size){
taskQueue->size=size;//有多少数量
taskQueue->total=taskQueue->head=taskQueue->tail=0;
taskQueue->data=(void**)calloc(size,sizeof(void *));
pthread_mutex_init(&taskQueue->mutex,NULL);
return ;
}
void task_queue_push(struct task_queue *taskQueue,void *data){
pthread_mutex_lock(&taskQueue->mutex);//共享区,只能允许一个人进去
if(taskQueue->total==taskQueue->size){
DBG(YELLOW"<push> : taskQueue is full\n"NONE);
pthread_mutex_unlock(&taskQueue->mutex);
return ;
}
taskQueue->data[taskQueue->tail]=data;
DBG(GREEN"<push> : "RED"data"NONE"is pushed!\n");
taskQueue->tail++;
taskQueue->total++;
if(taskQueue->tail==taskQueue->size){
DBG(YELLOW"<push> taskQueue reach tail!\n"NONE);
taskQueue->tail=0;
}
pthread_cond_signal(&taskQueue->cond);//等发信号
pthread_mutex_unlock(&taskQueue->mutex);//解锁
return;
}
void* task_queue_pop(struct task_queue *taskQueue){
pthread_mutex_lock(&taskQueue->mutex);
while(taskQueue->total==0){//惊群效应
//拿不到等着就行了,不要用轮询(非阻塞)
//手册写自动解锁,等待条件变量被发信号,线程执行会被挂起,不占cpu知道条件变量被发信号, 要在入口处被锁住,在返回前会重新锁上
pthread_cond_wait(&taskQueue->cond,&taskQueue->mutex);
}
void *data=taskQueue->data[taskQueue->head];
DBG(GREEN"<pop> :"BLUE"data"NONE"is poped!\n");
taskQueue->total--;
taskQueue->head++;
if(taskQueue->head==taskQueue->size){
DBG(YELLOW"<pop> taskQueue reach tail!\n"NONE);
taskQueue->head=0;
}
pthread_mutex_unlock(&taskQueue->mutex);
return data;
}
void *thread_run(void *arg){
pthread_detach(pthread_self());//线程分离
struct task_queue *taskQueue=(struct task_queue*)arg;//强转
while(1){
void *data=task_queue_pop(taskQueue);
printf("%s",(char*)data);
}
}
线程池
线程池的作用就是一直取任务队列中的数据,在这用输出来模拟做任务。
void *thread_run(void *arg){
pthread_detach(pthread_self());//线程分离
struct task_queue *taskQueue=(struct task_queue*)arg;//强转
while(1){
void *data=task_queue_pop(taskQueue);
printf("%s",(char*)data);
}
}
1.thread_pool_main.cc
主函数中使用一个文件指针去打开文件做任务,由于本机linux只是单核,那么INS就设置为1
开辟一个taskQueue的空间,对该taskQueue进行初始化,定义一个buff去存放指针,对线程进行创建初始化,使用fgets去读取并存入buff的位置,使用push函数去将taskqueue的指针存入buff[ind]的位置,再进行push,由于线程是在等待,那么每当线程空闲下来时可以直接插入。
#include"head.h"
#define INS 1
#define SIZE 1000
int main(){
FILE *fp;
pthread_t tid[INS];
char buff[SIZE][1024];
struct task_queue *taskQueue=(struct task_queue*)malloc(sizeof(struct task_queue));
task_queue_init(taskQueue,SIZE);
for(int i=0;i<INS;i++){
pthread_create(&tid[i],NULL,thread_run,(void *)taskQueue);
}
while(1){
if((fp=fopen("./1.thread_pool_main.cc","r"))==NULL){
perror("fopen");
exit(1);
}
int ind=0;//index
while(fgets(buff[ind],1024,fp)!=NULL){
task_queue_push(taskQueue,buff[ind]);
if(++ind==SIZE)ind=0;
usleep(10000);
}
fclose(fp);
}
return 0;
}