这是我所用的线程池,主任务为复制文件.首先是功能函数.c和.h文件

cp_dir.h

#ifndef __CP_DIR_H__
#define __CP_DIR_H__#include "head.h"
#include "pthread_pool.h"#define MAXNAME_LEN 4096
struct cp_file
{//要复制的文件char src[MAXNAME_LEN];//目标文件char dest[MAXNAME_LEN];
};
//将dir_src 指向的目录拷贝到dir_dest目录下
void cp_dir( pthread_pool *pool, char *dir_src, char *dir_dest);//普通函数-->实现两个文件之间的拷贝
void cp_file(void *arg);#endif

这是 cp_dir.c 文件

#include "cp_dir.h"//普通函数--->实现两个文件之间的拷贝
void cp_file(void *arg)
{//设置本线程为分离属性//pthread_detach(pthread_self());//将两个文件名解析出来struct cp_file *p = (struct cp_file *)arg;//以只读的方式去打开源文件int fd_src = open(p->src,O_RDONLY);if(fd_src == -1){printf("%s",p->src);fflush(stdout);perror("open src file failed");goto cp_return;}//以可读可写打开目标文件,如果目标文件不存在的话则创建,//如果目标文件存在的话则截短。int fd_dest = open(p->dest,O_RDWR | O_CREAT | O_TRUNC,0777);if(fd_dest == -1){perror("open dest file failed");goto cp_return;      }int ret;char buf[1024];//读取源文件的内容写入到目标文件中去while(1){ret = read(fd_src,buf,sizeof(buf));if(ret == 0){break;}else if(ret > 0){int w = write(fd_dest,buf,ret);if(w != ret){perror("write failed");}}else{perror("read error");break;}}cp_return:close(fd_dest);close(fd_src);free(p);
}//利用pool所指向的线程池将dir_src指向的目录拷贝到dir_dest目录下
void cp_dir(pthread_pool *pool,char *dir_src,char *dir_dest)
{//获取到要拷贝的目录的目录名(不带路径的)char *dirname = basename(dir_src);//dirname --> "20200907"//根据刚才获取到的目录名和目标路径合成目标目录(带路径)char newdirname[MAXNAME_LEN] = {0};sprintf(newdirname,"%s/%s",dir_dest,dirname);//newdirname -> "/mnt/hgfs/CS20201/20200907"printf("%s\n",newdirname);//先创建目标目录mkdir(newdirname,0777);dir_dest = newdirname;//打开要复制的目录DIR *dir = opendir(dir_src);if(dir == NULL){perror("open src dir failed");return ;}struct dirent *dirp = NULL;//读取目录项while(dirp = readdir(dir)){if(!strcmp(dirp->d_name,".") || !strcmp(dirp->d_name,"..")){continue;}char file_src[MAXNAME_LEN] = {0};//获取到目录项的名字(带路径)sprintf(file_src,"%s/%s",dir_src,dirp->d_name);printf("file_src : %s\n",file_src);//获取目录项的属性struct stat st;lstat(file_src,&st);//如果是普通文件或者是链接文件就直接创建一个线程去拷贝即可if(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)){//开辟一个线程去对两个文件实现直接拷贝char file_dest[MAXNAME_LEN] = {0};sprintf(file_dest,"%s/%s",dir_dest,dirp->d_name);//此时要把源文件和目标文件都传递给线程函数    //所以需要把这个目标文件和源文件都保存到一个结构体中//再把这个结构体传递给线程函数struct cp_file *cp = malloc(sizeof(*cp));strcpy(cp->src,file_src);strcpy(cp->dest,file_dest);printf("拷贝的两个文件名为:\n");printf("src_file : %s\n",file_src);printf("des_file : %s\n",file_dest);printf("--------------------------------------\n");/*//创建一个线程去执行copy的任务pthread_t tid;int ret = pthread_create(&tid,NULL,cp_file,(void*)cp);if(ret != 0){perror("pthread create failed");}//pthread_join(tid,NULL);*//*int ret = pthread_create(&tids[tid_num++],NULL,cp_file,(void*)cp);if(ret != 0){perror("pthread create failed");}*/add_task(pool,cp_file,(void *)cp);}else if(S_ISDIR(st.st_mode)){//如果是目录的话则递归调用字节printf("拷贝的两个目录名为:\n");printf("src_dir : %s\n",file_src);printf("des_dir : %s\n",dir_dest);printf("--------------------------------------\n");cp_dir(pool,file_src,dir_dest);}else{printf("%s is UNKOWN TYPE!\n",file_src);}}closedir(dir);return ;
}

线程池头文件

pthread_pool_copy.h

#ifndef __PTHREAD_POOL_H__
#define __PTHREAD_POOL_H__#include "head.h"//线程池中线程的最大数量
#define MAX_THREADS_NUM 50//线程池中任务队列最大的任务数量
#define MAX_TASKS 1000typedef struct pthread_pool
{//线程池的实现按照项目的不同也有所不同,但大体应该要有如下成员://因为“任务队列”是一种共享资源,所有我们需要互斥锁//就是说我们需要一把互斥锁去保存“任务队列”pthread_mutex_t lock;//同时当我们的任务队列中没有任务的时候,线程池内的线程应该要休眠//以避免系统资源的浪费,所有需要线程条件变量//线程条件变量用来表示“任务队列”中是否有任务pthread_cond_t cond;//任务队列(链表),指向第一个需要执行的任务//所有的线程都从这个任务链表中获取任务struct task *task_list;//指向线程ID的数组,用来保存线程池中所有线程的IDpthread_t *tids;//线程池中正在服役的线程数--->线程的个数unsigned int active_threads;//线程池中任务队列最大的任务数量unsigned int max_waiting_tasks;//线程池中任务队列当前的任务数量unsigned int cur_waiting_tasks;//表示是否退出程序bool shutdown;//bool-->true(1) false(0)
}pthread_pool;//任务节点
struct task
{//每一个任务结点保存一个任务.//所谓任务实际上就是把一个文件或目录从源目录下拷贝到目标目录下//那么这个任务该如何去保存到任务节点上面去?//任务的完成是通过函数来实现的,所以我们如果要去保存一个任务的话//只需要保存完成任务的函数(cp_file)的指针既可以了。//也就是说如果我们要去完成一个任务,就是去节点保存的地址上去//执行一个函数就可以啦,函数只要执行完了,任务也就完成了。//那么我们就需要定义一个函数指针。保存任务函数(cp_file)的地址void (*do_task)(void *arg);//另外,我们可能需要给任务函数传递参数(比如:文件名)void *arg;//下一个任务struct task *next;
};/*init_pool:线程池的初始化函数初始化指定的线程池的,线程池中有thread_num个初始化线程。@pool:线程池指针,指向你要初始化的线程池@thread_num:你要初始化的线程池中一开始线程的数量@返回值:成功返回0,失败返回-1。
*/
int init_pool(phtread_pool *pool,unsigned int thread_num);/*routine:任务调配函数--->线程函数所有线程开始都回去执行这个函数,此函数会不断的从线程池的任务队列中取任务然后交给线程去执行取任务--->arg表示的是线程池的指针,在线程池中有任务队列,任务队列中有任务节点,每一个任务节点中都包含了函数指针和函数参数。
*/
void *routine(void *arg);/*销毁线程池:销毁线程池前要保证所有的任务都已经完成了
*/
int destroy_pool(pthread_pool *pool);/*add_task:给任务队列增加任务,把do_task指向的任务(函数指针)和arg指向的参数保存到一个任务节点中去,同时将任务节点添加到pool表示的线程池的任务队列中去。@pool:指针,指向你要添加的任务的线程池@fun_task:你要添加的任务(cp_file)@fun_arg:你要执行的任务的参数(两个文件的文件名)@返回值:成功返回0,失败返回-1。
*/
int add_task(pthread_pool *pool,void (*fun_task)(void *arg),void *fun_arg);//往线程池中添加线程
int add_threads(pthread_pool *pool,unsigned int add_threads_num);//删除线程池中的线程
int remove_threads(pthread_pool *pool,unsigned int remove_threads_num);#endif

线程池函数

#include "pthread_pool.h"/*init_pool:线程池的初始化函数初始化指定的线程池的,线程池中有thread_num个初始化线程。@pool:线程池指针,指向你要初始化的线程池@thread_num:你要初始化的线程池中一开始线程的数量@返回值:成功返回0,失败返回-1。
*/
int init_pool(pthread_pool *pool,unsigned int thread_num)
{//初始化线程池结构体//初始化线程互斥锁pthread_mutex_init(&pool->lock,NULL);//初始化线程条件变量pthread_cond_init(&pool->cond,NULL);//创建一个任务节点,并且使用task_list去指向它//需要注意的是第一个任务节点时没有初始化的,即没有值的//因为对线程池进行初始化的时候,此时还没有任务pool->task_list = (struct task *)malloc(sizeof(struct task));pool->task_list->next = NULL;//指向线程ID的数组进行初始化,此数组中保存了线程池中所有线程的ID//用来保存线程ID的数组空间最好是用malloc进行开辟而且这块要足够大pool->tids = (pthread_t *)malloc(sizeof(pthread_t)*MAX_THREADS_NUM);if(pool->task_list == NULL || pool->tids == NULL){perror("malloc memory failed");return -1;}//线程池中正在服役的线程数量-->总的线程个数pool->active_threads = thread_num;//线程池中任务队列最大的任务数量pool->max_waiting_tasks = MAX_TASKS;//线程池中任务队列当前的任务数量pool->cur_waiting_tasks = 0;//不退出pool->shutdown = false;//创建thread_num个线程,同时记录所有线程的ID,让所有线程//一开始就执行任务调配函数去执行任务。int i;for(i = 0;i < thread_num;i++){if(pthread_create(&pool->tids[i],NULL,routine,(void *)pool) != 0){perror("create thread failed");return -1;}//打印调试信息printf("[%lu] create tids[%d] : [%lu] is success!\n",pthread_self(),i,pool->tids[i]);}return 0;
}//清理函数--->防止某一个线程带锁退出
void handler(void *arg)
{pthread_mutex_unlock((pthread_mutex_t *)arg);
}/*routine:任务调配函数--->线程函数所有线程开始都回去执行这个函数,此函数会不断的从线程池的任务队列中取任务然后交给线程去执行取任务--->arg表示的是线程池的指针,在线程池中有任务队列,任务队列中有任务节点,每一个任务节点中都包含了函数指针和函数参数。
*/
void *routine(void *arg)
{struct task *p = NULL;//因为这个函数需要去访问任务队列,任务队列是共享资源//所以需要上锁//arg是你的线程池的指针.pthread_pool *pool = (pthread_pool *)arg;while(1){//在上锁之前可以设置一个线程退出清理函数//当线程因为某种意外情况导致线程夭折的时候自动执行//我指定的清理函数(一般是解锁为了防止线程带锁退出)//只能用在线程中pthread_cleanup_push(handler,(void *)&pool->lock);//获取到线程互斥锁,上锁pthread_mutex_lock(&pool->lock);//任务队列的情况分为很多种://1.任务队列没有任务并且线程池还没有结束的时候//此时,在没有任务的时候(条件不满足的情况下),需要等待新任务的到来//此处不能用if,必须要用while,因为一个线程被唤醒之后,有可能"抢不到"任务while(pool->cur_waiting_tasks == 0 && !pool->shutdown){//当前线程陷入休眠pthread_cond_wait(&pool->cond,&pool->lock);//当前线程被唤醒的条件://1.当有新任务的时候 2.当我要销毁线程池的时候}//2.任务队列中没有任务并且线程池要结束if(pool->cur_waiting_tasks == 0 && pool->shutdown){//解锁pthread_mutex_unlock(&pool->lock);pthread_exit(NULL);}//3.任务队列中有任务,将任务节点从任务链表中取下来//找到任务链表中的第二个节点,因为第一个节点上面是空节点,没有任务p = pool->task_list->next;//摘除结点,更新链表pool->task_list->next = p->next;p->next = NULL;pool->cur_waiting_tasks--;//获取到线程互斥锁,解锁pthread_mutex_unlock(&pool->lock);//清理线程退出函数pthread_cleanup_pop(0);//当条件满足(任务队列中有任务的)的时候,去执行任务.//执行拷贝任务的时候,拷贝任务是不能被打断的,所以我们要设置//线程属性为不可被取消(干掉)pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);//按任务节点中的任务去执行任务(去执行cp_file函数)(p->do_task)(p->arg);//取消线程的不可被取消的属性pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//释放任务节点free(p);}
}/*销毁线程池:销毁线程池前要保证所有的任务都已经完成了
*/
int destroy_pool(pthread_pool *pool)
{//释放所有的空间等待任务执行完毕pool->shutdown = true;//线程池要结束了//唤醒所有的线程pthread_cond_broadcast(&pool->cond);   //利用join函数回收每一个线程int i;for(i = 0;i < pool->active_threads;i++){//从第0个线程开始回收int r = pthread_join(pool->tids[i],NULL);if(r != 0){printf("Recover tids[%d] failed!\n",i);}else{printf("Recover tids[%d] success!\n",i);          }}//销毁线程互斥锁pthread_mutex_destroy(&pool->lock);//销毁线程条件变量pthread_cond_destroy(&pool->cond);//释放任务节点if(pool->task_list){free(pool->task_list);//只需要释放第一个节点即可}free(pool->tids);free(pool);return 0;
}/*add_task:给任务队列增加任务,把do_task指向的任务(函数指针)和arg指向的参数保存到一个任务节点中去,同时将任务节点添加到pool表示的线程池的任务队列中去。@pool:指针,指向你要添加的任务的线程池@fun_task:你要添加的任务(cp_file)@fun_arg:你要执行的任务的参数(两个文件的文件名)@返回值:成功返回0,失败返回-1。
*/
int add_task(pthread_pool *pool,void (*fun_task)(void *arg),void *fun_arg)
{//在往任务队列中添加任务的时候,需要注意解锁和上锁//加入任务后要唤醒等待的线程//创建一个新的节点去保存任务struct task *new_task = malloc(sizeof(*new_task));new_task->next = NULL;new_task->do_task = fun_task;new_task->arg = fun_arg;//把任务节点添加到任务链表中去pthread_mutex_lock(&pool->lock);//任务队列中的任务数量已达上限if(pool->cur_waiting_tasks >= MAX_TASKS){pthread_mutex_unlock(&pool->lock);printf("Too many task,add task failed!\n");//释放任务节点空间free(new_task->arg);free(new_task);return -1;}//任务节点采用尾插法插入任务链表(此处不能头插)struct task *find = pool->task_list;while(find->next){find = find->next;}//已经找到尾结点直接添加任务到链表中即可find->next = new_task;pool->cur_waiting_tasks++;//访问完任务链表完之后解锁pthread_mutex_unlock(&pool->lock);//添加完之后,唤醒线程pthread_cond_signal(&pool->cond);return 0;
}//往线程池中添加线程
int add_threads(pthread_pool *pool,unsigned int add_threads_num)
{}//删除线程池中的线程
int remove_threads(pthread_pool *pool,unsigned int remove_threads_num)
{}

主函数头文件

#ifndef __HEAD_H__
#define __HEAD_H__#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <libgen.h>#endif

主函数

#include "cp_dir.h"//./a.out dir_src dir_dest
int main(int argc,char *argv[])
{if(argc != 3){printf("Usage : %s <dir_src> <dir_dest>!\n",argv[0]);return -1;}//创建一个线程池pthread_pool *pool = malloc(sizeof(*pool));//初始化线程池int ret = init_pool(pool);if(ret == -1){printf("init pthread pool failed!\n");return -1;}//利用线程池实现目录之间的拷贝cp_dir(pool,argv[1],argv[2]);destroy_pool(pool);return 0;
}

martin_pthread_pool相关推荐

最新文章

  1. 计算两个时间的间隔时间是多少
  2. java 庖丁解牛api_Java Restful API Best Practices
  3. 6、linux网络编程--UDP协议编程
  4. 码农·如何成为一位数据科学家(第17期)pdf
  5. 基于bert模型的文本分类研究:“Predict the Happiness”挑战
  6. 【ABP杂烩】面向切面编程(AOP)知识总结
  7. rxjs ThrottleTime 和 debounceTime 的操作符区别
  8. python批量打印word_Python使用扩展库pywin32实现批量文档打印实例
  9. android简单的夜间模式
  10. selenium 2定位方式实例
  11. 论文笔记:EPNet: Enhancing Point Features with Image Semantics for 3D Object Detection
  12. pytthon问题 pytcharm Automatic upload failed: could not resolve file “sftp://10.xx.xx.xx 【已解决】
  13. 国产某偶像剧天才程序员爱心C语言作业用C++写的代码,结果是Py脚本文件
  14. 从键盘输入的年份,判断该年是否是闰年
  15. 倍斯特快人一步 执着快充移动电源
  16. 微分平坦(differential flatness)
  17. 咱们500万条数据测试一下,如何合理使用索引加速?
  18. 利用计算机模拟专家给病人沴,【计算机】模拟部分.ppt
  19. 谭铁牛院士荣获国际模式识别最高奖
  20. 已知三角形三个顶点坐标求面积(简单办法)

热门文章

  1. Android开发之虹软人脸识别活体检测SDK包Bitmap转NV21方法
  2. 打字机已经被计算机所取代用英语,无法被电脑所取代的职业
  3. LeetCode 410——分割数组的最大值
  4. centos7上配置Samba服务器完成与windows的文件共享
  5. 完全优化MySQL数据库性能的八大巧方法
  6. C#基础第三天-作业-集合-冒泡排序-模拟名片
  7. linux下Oracle 10g安装(超级详细图解教程)
  8. [SoapUI] Jenkins 配置不同环境(TP, LIVE)
  9. Ruby on Rails 终极部署方案 nginx+mina+puma
  10. Java线程:创建与启动