#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;iactive_threads;i++) { if(pthread_create(&(pool->tids[i]),NULL,routine,(void *)pool)!=0) { perror("线程创建失败\n"); return false; } } //12.线程初始化成功,返回true return true; }

//增添任务 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;iactive_threads;i++) { errno=pthread_join(pool->tids[i],NULL); if(errno!=0)//不为0说明接合失败 { printf("join tids[%d] error: %s\n",i,strerror(errno)); } else { printf('[%u] is joined\n', (unsigned)pool->tids[i]); } } //4.释放线程池中动态开辟的一些空间 free(pool->task_list); free(pool->tids); free(pool); //成功销毁,返回true return true; }

//目录复制 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 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录