线程池概念?
顾名思义线程池就是很多线程的集合。需要的时候让其中的一些线程来执行任务。
为什么需要线程池?
线程的创建和销毁是需要时间的。尤其对于并发量比较大的服务器,单个任务比较简单的场合下,每次都为新的任务创建线程,任务结束后再销毁线程,这种做法比较不划算。
为了解决这个问题,可以在一开始就创建n个线程。当接到任务时,就将新的任务委派给已存在的n个线程中的几个去执行。这些线程完成任务之后,又会回到等待状态。
线程池的操作:
1.初始化
将线程池结构体的成员进行初始化。
队列初始化
创建n个线程,然后阻塞线程。
线程被唤醒后,从任务队列里取出任务,然后从中找出要执行的函数,以及要带进去的参数,然后执行该函数,任务数量减减。
2.添加任务
将任务添加到待执行的任务队列里。
任务数量加加
然后唤醒线程。
问题:创建一个简单线程池。并且利用该线程池进行文件复制。
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <error.h>
#include <pthread.h>
#include <sys/queue.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
//线程池中线程总数为50
#define THREADS_TOTAL 50
//定义一个任务结构体
struct _task{
void *(*task_2_run)(void *); //指向函数的指针
void *arg; //该函数的参数
//另外还要保存下一个任务结构体的地址
SIMPLEQ_ENTRY(_task) entry;
//上面的这句替换之后将变成
// struct {
// struct _task *sqe_next;
// } entry;
};
//线程池总结构体
struct {
pthread_mutex_t mutex; //互斥锁
pthread_cond_t cond; //条件变量
pthread_t *thread; //线程数组的头地址
SIMPLEQ_HEAD(,_task) taskq_head; //待执行任务的队列
//经宏定义替换后
// struct {
// struct _task *sqh_first;
// struct _task **sqh_last;
// }taskq_head;
int tasks; //待执行任务的数量
}pool;
//线程创建需要的函数,所有的线程都运行这个函数
void *taskhandler(void *arg)
{
printf("thread %u running\n",pthread_self());
while(1)
{
//互斥锁上锁
pthread_mutex_lock(&pool.mutex);
if(pool.tasks==0)
{
pthread_cond_wait(&pool.cond,&pool.mutex);
}
//被唤醒后,从任务队列里取出任务,然后执行
//取出任务
struct _task *tasktmp=SIMPLEQ_FIRST(&pool.taskq_head);
if(tasktmp!=NULL)
{
//从队列中除去第一个结点
SIMPLEQ_REMOVE_HEAD(&pool.taskq_head,entry);
//将待执行任务数量减1
if(pool.tasks>0)
pool.tasks--;
}
//互斥锁解锁
pthread_mutex_unlock(&pool.mutex);
if(tasktmp!=NULL)
{
//直接运行任务,代进结构体里的参数成员
tasktmp->task_2_run(tasktmp->arg);
}
}
}
//将任务加到任务队列里
void pool_addtask(void *(*task_2_add)(void *),void *arg)
{
//先上锁
pthread_mutex_lock(&pool.mutex);
//先构造一个结点
struct _task *tasktmp=malloc(sizeof(struct _task));
tasktmp->task_2_run=task_2_add;//将参数中的函数名放入节点
tasktmp->arg=arg;//将参数中的arg放入节点
//将节点插入队列的尾部
SIMPLEQ_INSERT_TAIL(&pool.taskq_head,tasktmp,entry);
//将待执行任务的数量加1
pool.tasks++;
//叫醒线程池中阻塞睡眠的线程
pthread_cond_signal(&pool.cond);
pthread_mutex_unlock(&pool.mutex);
}
//线程池初始化
void pool_init(void)
{
//互斥锁的初始化
pthread_mutex_init(&pool.mutex,NULL);
//条件变量的初始化
pthread_cond_init(&pool.cond,NULL);
//动态创建pthread_t的数组
pool.thread=malloc(sizeof(pthread_t)*THREADS_TOTAL);
//初始化队列
SIMPLEQ_INIT(&pool.taskq_head);
//让待执行任务的数量为0
pool.tasks=0;
//创建线程
int i;
for(i=0;i<THREADS_TOTAL;i++)
{
pthread_create(&pool.thread[i],NULL,taskhandler,NULL);
}
}
//定义拷贝结构体
struct _copy{
char *addr_dest;//目标地址
char *addr_sour; //源地址
int size; //拷贝的长度
};
//复制任务
void *copytask(void *arg)
{
struct _copy *tmp=arg;
printf("dest: %x,source:%x,size:%d\n",tmp->addr_dest,tmp->addr_sour,tmp->size);
memcpy(tmp->addr_dest,tmp->addr_sour,tmp->size);
msync(tmp->addr_dest,512,MS_SYNC);
}
//argv[1]是源文件的名字,argv[2]是目标文件的名字
int main(int argc,const char *argv[])
{
if(argc<3)
{
printf("用法:./pool 源文件名 目标文件名");
exit(EXIT_FAILURE);
}
pool_init();
//打开源文件并映射
int fd=open(argv[1],O_RDONLY);
if(fd<0)
{
perror("open source file");
exit(EXIT_FAILURE);
}
struct stat st;
fstat(fd,&st);
char *addr_s=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,fd,0);
if(addr_s==MAP_FAILED)
{
perror("map source file");
exit(EXIT_FAILURE);
}
close(fd);
//创建新文件并映射
fd=open(argv[2],O_CREAT|O_RDWR|O_TRUNC,666);
if(fd<0)
{
perror("open dest file");
exit(EXIT_FAILURE);
}
ftruncate(fd,st.st_size);
char *addr_d=mmap(NULL,st.st_size,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
if(addr_d==MAP_FAILED)
{
perror("map dest file");
exit(EXIT_FAILURE);
}
close(fd);
int i;
struct _copy tmp;
for(i=0;i<st.st_blocks;i++)
{
tmp.addr_dest=addr_d+i*512;
tmp.addr_sour=addr_s+i*512;
if(st.st_blocks==1)
{
tmp.size=st.st_size;
}
else if(i<st.st_blocks-1)
{
tmp.size=512;
}
else
{
tmp.size=st.st_size-i*512;
}
//将copytask函数以及我们的参数一起传给pool_addtask
pool_addtask(copytask,&tmp);
}
sleep(20);
//munmap(addr_s,st.st_size);
//munmap(addr_d,st.st_size);
while(1);
return 0;
}