使用线程池实现高效目录复制
#include 'myhead.h'
//清除函数 void handler(void *arg) { printf('[%u] is ended.\n', (unsigned)pthread_self());
//解锁
pthread_mutex_unlock((pthread_mutex_t *)arg);
}
//线程执行函数 void *routine(void *arg) { //1.先接住线程池的地址 thread_pool *pool=(thread_pool *)arg; struct task *p; //2.执行操作 while(1) { //2.线程取消例程函数 //将来要是有人想取消一个线程,要先把线程拥有的锁解开,再响应取消 pthread_cleanup_push(handler,(void *)&pool->lock); //3.上锁,临界资源->任务队列 pthread_mutex_lock(&pool->lock); //4.如果当前线程池没有关闭,并且当前线程池没有任务做 while(pool->waiting_tasks == 0 && !pool->shutdown) { //那么就进去条件变量中睡觉。 //printf('[%u] ==>睡\n',(unsigned)pthread_self()); pthread_cond_wait(&pool->cond, &pool->lock); //线程睡眠,自动解锁
}
//5.要是线程池关闭了,或者有任务做,这些线程就会运行到这行代码
//判断当前线程池是不是关闭了,并且没有等待的任务
if(pool->waiting_tasks == 0 && pool->shutdown == true)
{
//如果线程池关闭,又没有任务做
//线程那么就会解锁
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL);
}
//6.能运行到这里,说明有任务做
//p指向头节点的下一个
p = pool->task_list->next;
//7.让头节点的指针域指向p的下一个节点
pool->task_list->next = p->next;
//8.当前任务个数-1
pool->waiting_tasks--;
//9.解锁
pthread_mutex_unlock(&pool->lock);
//10.删除线程取消例程函数
pthread_cleanup_pop(0);
//11.线程要执行任务函数,设置线程不能响应取消
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
//执行这个p节点指向的节点的函数
(p->do_task)(p->arg);
//任务函数执行完毕,设置线程能响应取消
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
//执行完了,释放p对应的空间
free(p);
}
pthread_exit(NULL);
}
//初始化线程池
bool init_pool(thread_pool *pool,unsigned int threads_number)
{
//1.初始化线程池互斥锁
pthread_mutex_init(&pool->lock,NULL);
//2.初始化条件变量
pthread_cond_init(&pool->cond,NULL);
//3.初始化标志位为false,代表当前线程池正在运行
pool->shutdown=false;
//4.初始化任务队列头节点
pool->task_list=malloc(sizeof(struct task));
//5.为存储线程ID号tids申请空间
pool->tids=malloc(sizeof(pthread_t)*MAX_ACTIVE_THREADS);
//6.对第四步和第五步做错误判断
if(pool->task_list==NULL||pool->tids==NULL)
{
perror("任务队列|线程号指针 内存失败\n");
return false;
}
//7.为线程池任务队列头节点指针域赋值NULL
pool->task_list->next==NULL;
//8.使用宏定义来设置线程池最大任务个数为1000
pool->max_waiting_tasks=MAX_WAITING_TASKS;
//9.设置当前需要处理任务数位0
pool->waiting_tasks=0;
//10.初始化线程池中线程的个数
pool->active_threads=threads_number;
//11.创建线程
for(int i=0;i
//增添任务 bool add_task(thread_pool *pool,void *(*do_task)(void *arg),void *arg) { //1.为新任务的节点申请空间 struct task *new_task=malloc(sizeof(struct task)); if(new_task==NULL) { perror("新任务申请空间失败\n"); return false; } //2.为新节点的数据域与指针域赋值 new_task->do_task=do_task; new_task->arg=arg; new_task->next=NULL; //3.在添加任务到任务队列前,必须先上锁,因为添加任务属于访问临界资源->即任务队列 pthread_mutex_lock(&pool->lock); //4.如果当前需要等待处理的任务个数已经超过或等于1000,就解锁,并释放掉全部刚刚创建的新节点 if(pool->waiting_tasks>=MAX_WAITING_TASKS) { pthread_mutex_unlock(&pool->lock); fprintf(stderr,"任务过多\n"); free(new_task); return false; } //5.寻找任务队列中的最后一个节点 struct task *tmp=pool->task_list; while(tmp->next!=NULL) { tmp=tmp->next; } //6.尾插到任务队列中 tmp->next=new_task; //7.将当前需要等待处理的任务个数+1 pool->waiting_tasks++; //8.新任务添加完毕,解锁 pthread_mutex_unlock(&pool->lock); //9.任务已添加,随机唤醒一个条件变量中睡眠的线程起来做任务 pthread_cond_signal(&pool->cond); //添加成功,唤醒成功,返回true return true; }
//添加线程 int add_thread(thread_pool *pool,unsigned additional_threads) { //1.如果创建条数为0,则不添加,直接返回0 if(additional_threads==0) { return 0; } //2.将初始化的线程个数与新添加的线程个数相加赋值给总线程条数 unsigned total_threads=pool->active_threads+additional_threads; //3.循环create创建新线程,并将实际创建条数+1,若创建后实际创建成功的线程条数为0,则退出返回-1 int i=0,actual_increment=0;//实际创建条数 for(i=pool->active_threads;i<total_threads&&i<MAX_ACTIVE_THREADS;i++) { if(pthread_create(&(pool->tids[i]),NULL,routine,(void *)pool)!=0) { perror("新线程创建失败\n"); return false; } } //4.将实际创建成功个数加入需要等待处理的线程条数 pool->active_threads+=actual_increment; //5.返回真正创建的条数 return actual_increment; }
//取消删除线程 int remove_thread(thread_pool *pool,unsigned int removing_threads) { //1.如果想删0条,不给删,直接返回剩余条数 if(removing_threads==0) { return pool->active_threads;//这里相当于剩余条数 } //2.求出取消后剩余线程条数 int remaining_threads=pool->active_threads-removing_threads; remaining_threads = remaining_threads > 0 ? remaining_threads : 1; //3.循环取消 int i=0; for(i=pool->active_threads-1;i>remaining_threads-1;i--) { errno=pthread_cancel(pool->tids[i]); if(errno!=0)//不为0说明删除失败 { break; } } //i不变不减小,说明删除失败,然后就返回 if(i==pool->active_threads-1) { return -1; } else { pool->active_threads=i+1;//i为下标,下标+1为实际剩余条数 } return i+1;//返回删除后剩余条数 }
//销毁线程池
bool destroy_pool(thread_pool *pool)
{
//1.设置线程池为关闭
pool->shutdown=false;
//2.唤醒所有睡眠的线程,目的:方便后续让线程退出
pthread_cond_broadcast(&pool->cond);
//3.结合所有线程
int i=0;
for(i=0;i
//目录复制 int copy_any() { char source_dir[200];//源路径 char copy_dir[200];//复制地路径 char source_backdirname[50];//源路径初始文件夹名反序 char source_dirname[50];//源路径初始文件夹名正序 while(1) { bzero(source_dir,sizeof(source_dir)); bzero(copy_dir,sizeof(copy_dir)); bzero(source_backdirname,sizeof(source_backdirname)); bzero(source_dirname,sizeof(source_dirname)); printf("请输入需要复制的源路径:"); scanf("%s",source_dir); printf("请输入复制地路径:"); scanf("%s",copy_dir); printf("源1:%s\n",source_dir); printf("复制1:%s\n",copy_dir); int j=0; for(int i=strlen(source_dir)-1;source_dir[i]!='/';i--)//文件夹名反序到source_backdirname中 { source_backdirname[j]=source_dir[i]; j++; } j=0; printf("strlen(source_backdirname):%ld\n",strlen(source_backdirname)); for(int i=strlen(source_backdirname)-1;i>=0;i--)//正序输入到source_dirname中 { source_dirname[j]=source_backdirname[i]; j++; } struct file_path *init_path = malloc(sizeof(file_path)); //为文件路径结构体开辟内存空间 memset(init_path,0,sizeof(file_path));//对内存清零 sprintf(init_path->source_path,"%s",source_dir);//拼凑源文件路径 sprintf(init_path->copy_path,"%s/%s",copy_dir,source_dirname);//拼凑目标文件路径 printf("源:%s\n",init_path->source_path); printf("复制:%s\n",init_path->copy_path); copy_wholedir(init_path); free(init_path); return 0; } }
//复制文件夹中单个文件 void *copy_single(void *arg) { struct file_path *tmp_path=(struct file_path *)arg; char buf[10000]; bzero(buf,sizeof(buf)); printf("源目录单文件:%s\n",tmp_path->source_path); int fd3=open(tmp_path->source_path,O_RDWR);//打开源文件 if(fd3==-1) { printf("源目录中单文件打开失败\n"); return NULL; } long len=lseek(fd3,0,SEEK_END);//读取文件字节总数 lseek(fd3,0,SEEK_SET);//将光标重新移至开头 read(fd3,buf,len);//读取内容,存入buf
int fd4=open(tmp_path->copy_path,O_RDWR|O_CREAT,0777);//创建并打开复制文件
if(fd4==-1)
{
printf("复制地目录中单文件打开失败\n");
return NULL;
}
write(fd4,buf,len);//写入内容
close(fd3);//关闭文件
close(fd4);//关闭文件
return NULL;
}
//复制整个文件夹 int copy_wholedir(struct file_path *init_path) { mkdir(init_path->copy_path,0777); DIR *dp3=opendir(init_path->copy_path);//打开复制地路径目录 if(dp3==NULL) { printf("复制地路径不存在,请重新输入\n"); return 0; }
DIR *dp2=opendir(init_path->source_path);//打开源路径目录
if(dp2==NULL)
{
printf("源路径不存在,请重新输入\n");
return 0;
}
chdir(init_path->source_path);//切换到源路径目录
struct dirent *ep2=NULL;
while(1)
{
ep2 = readdir(dp2);//读取路径下的文件内容
if(ep2 == NULL)//读完了也没找到,退出
{
break;
}
if(ep2->d_name[0] == '.')//去掉.和..
{
continue;
}
struct file_path *tmp_path = malloc(sizeof(file_path)); //为文件结构体开辟内存空间
memset(tmp_path,0,sizeof(file_path));//对内存清零
sprintf(tmp_path->source_path,"%s/%s",init_path->source_path,ep2->d_name);//拼凑源文件路径
sprintf(tmp_path->copy_path,"%s/%s",init_path->copy_path,ep2->d_name);//拼凑目标文件路径
if(ep2->d_type==4)
{
//mkdir(tmp_path->copy_path,0777);//在复制地路径下创建与源路径同名文件夹
copy_wholedir(tmp_path);
free(tmp_path);
}
if(ep2->d_type==8)
{
printf("ep2->d_name:%s\n",ep2->d_name);
printf("源目录文件为:%s\n",tmp_path->source_path);
//printf("复制地目录文件为:%s",tmp_path->copy_path);
add_task(pool,copy_single,(void *)tmp_path);
}
}
//==free(tmp_path);
closedir(dp2);
closedir(dp3);
}
//这段代码会发生堆栈溢出的原因是因为在复制整个文件夹的过程中,每个文件的复制都会创建一个新的线程来处理,但是没有限制线程的数量。当文件夹中的文件数量过多时,会创建过多的线程,导致线程栈空间不足,从而发生堆栈溢出。
//解决这个问题的方法是在创建线程之前,先判断线程池中活跃线程的数量是否已经达到了最大限制,如果达到了最大限制,就不再创建新的线程,而是将任务放入任务队列中等待线程池中的线程来处理。这样可以避免创建过多的线程,节省线程栈空间,从而解决堆栈溢出的问题。
原文地址: https://www.cveoy.top/t/topic/okZm 著作权归作者所有。请勿转载和采集!