【Linux】线程并发拷贝程序

来源:互联网 发布:移动端优化 编辑:程序博客网 时间:2024/05/17 02:07

据说大连某211高校的李教授越来越重口,不仅延续要求他所带的每一个本科班,都要写一份线程并发拷贝程序的传统,而且还开始规定不能用Java语言写作,导致我之前写的《【Java】线程并发拷贝程序》(点击打开链接)作废。所有李教授旗下的学生,必须在毫无图形界面的Linux系统,用里面vi去写作。这更让莘莘学子们感觉本来头来就不光明的天空更加黑暗起来。

更重要的是,若干年过去了,网上对其的研究与资料,依旧是少数,依旧是那份流传已久,以讹传讹的C语言版。

虽然这个程序毫无研究价值,但是本着治病救人,同时借此深入研究LinuxC的线程编程机制,我再一次完成了这份,在Linux用最最最纯种C语言完成的线程并发拷贝程序。

如下图,搞了3个线程在Linux系统下完成文件夹A中的所有内容到空空是也文件夹B的复制。同时按照李教授的喜好,在前面补个前缀,也就是重命名目标文件夹。


可能有些同学说我上面的图形化的Linux系统与无图形化的Linux是不同的。

其实上述代码在各个版本的Linux系统都可以运行的,只是你写C语言要用《【Linux】vi/vim的使用》(点击打开链接)去写,查询文件与文件夹,要用cd命令先进入相关的路径,同时用dir命令读这个文件夹目录。要是你觉得vi难用,可以用《【Linux】用Winscp远程访问无图形界面的Linux系统》(点击打开链接),将你在windows下写好的代码,传上Linux,再用gcc编译再运行。

具体代码如下:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<dirent.h>//输出文件信息#include<sys/stat.h>//判断是否目录#include<pthread.h>//使用线程char *source_arr[512];//存放源文件路径的数组char *destination_arr[512];//存放目标文件路径的数组int source_arr_index=0;//存放源文件路径的数组的索引,就是for(int i=xx;..;..)那个iint destination_arr_index=0;//存放目标文件路径的数组的索引pthread_mutex_t mutex;//声明一个互斥锁mutexint i=0;//多个线程函数用到这个i,用于记录是否复制完毕,因此作为全局变量处理~/*字符串处理函数*/int endwith(char* s,char c){//用于判断字符串结尾是否为“/”与“.”if(s[strlen(s)-1]==c){return 1;}else{return 0;}}char* str_contact(char* str1,char* str2){//字符串连接char* result;result=(char*)malloc(strlen(str1)+strlen(str2)+1);//str1的长度+str2的长度+\0;if(!result){//如果内存动态分配失败printf("字符串连接时,内存动态分配失败\n");exit(1);}strcat(result,str1);strcat(result,str2);//字符串拼接return result;}/*遍历函数*/int is_dir(char* path){//判断是否是目录struct stat st;stat(path,&st);if(S_ISDIR(st.st_mode)){return 1;}else{return 0;}}void read_folder(char* source_path,char *destination_path){//复制文件夹if(!opendir(destination_path)){if (mkdir(destination_path,0777))//如果不存在就用mkdir函数来创建{    printf("创建文件夹失败!");}}char *path;path=(char*)malloc(512);//相当于其它语言的String path="",纯C环境下的字符串必须自己管理大小,这里为path直接申请512的位置的空间,用于目录的拼接path=str_contact(path,source_path);//这三句,相当于path=source_pathstruct dirent* filename;DIR* dp=opendir(path);//用DIR指针指向这个文件夹while(filename=readdir(dp)){//遍历DIR指针指向的文件夹,也就是文件数组。memset(path,0,sizeof(path));path=str_contact(path,source_path);//如果source_path,destination_path以路径分隔符结尾,那么source_path/,destination_path/直接作路径即可 //否则要在source_path,destination_path后面补个路径分隔符再加文件名,谁知道你传递过来的参数是f:/a还是f:/a/啊?char *file_source_path;file_source_path=(char*)malloc(512);file_source_path=str_contact(file_source_path,source_path);if(!endwith(source_path,'/')){file_source_path=str_contact(source_path,"/");}char *file_destination_path;file_destination_path=(char*)malloc(512);file_destination_path=str_contact(file_destination_path,destination_path);if(!endwith(destination_path,'/')){file_destination_path=str_contact(destination_path,"/");}//取文件名与当前文件夹拼接成一个完整的路径file_source_path=str_contact(file_source_path,filename->d_name);if(is_dir(file_source_path)){//如果是目录if(!endwith(file_source_path,'.')){//同时并不以.结尾,因为Linux在所有文件夹都有一个.文件夹用于连接上一级目录,必须剔除,否则进行递归的话,后果无法想象!file_destination_path=str_contact(file_destination_path,filename->d_name);//对目标文件夹的处理,取文件名与当前文件夹拼接成一个完整的路径read_folder(file_source_path,file_destination_path);//进行递归调用,相当于进入这个文件夹进行遍历~}}else{//否则,将源文件于目标文件的路径分别存入相关数组//对目标文件夹的处理,取文件名与当前文件夹拼接成一个完整的路径file_destination_path=str_contact(file_destination_path,"前缀_");//给目标文件重命名,这里示意如何加个前缀~^_^file_destination_path=str_contact(file_destination_path,filename->d_name);source_arr[source_arr_index]=file_source_path;source_arr_index++;destination_arr[destination_arr_index]=file_destination_path;destination_arr_index++;}}}/*复制函数*/void copy_file(char* source_path,char *destination_path){//复制文件char buffer[1024];FILE *in,*out;//定义两个文件流,分别用于文件的读取和写入int len;if((in=fopen(source_path,"r"))==NULL){//打开源文件的文件流printf("源文件打开失败!\n");exit(1);}if((out=fopen(destination_path,"w"))==NULL){//打开目标文件的文件流printf("目标文件创建失败!\n");exit(1);}int len;//len为fread读到的字节长while((len=fread(buffer,1,1024,in))>0){//从源文件中读取数据并放到缓冲区中,第二个参数1也可以写成sizeof(char)fwrite(buffer,1,len,out);//将缓冲区的数据写到目标文件中}fclose(out);fclose(in);}/*线程执行函数*/void *thread_function(void *arg){while(i<destination_arr_index){  if(pthread_mutex_lock(&mutex)!=0){//对互斥锁上锁,临界区开始  printf("%s的互斥锁创建失败!\n",(char *)arg);  pthread_exit(NULL);  }  if(i<destination_arr_index){copy_file(source_arr[i],destination_arr[i]);//复制单一文件printf("%s复制%s到%s成功!\n",(char *)arg,source_arr[i],destination_arr[i]);i++;sleep(1);//该线程挂起1秒  }  else{//否则退出  pthread_exit(NULL);//退出线程  }  pthread_mutex_unlock(&mutex);//解锁,临界区结束  sleep(1);//该线程挂起1秒  }  pthread_exit(NULL);//退出线程  }  /*主函数*/int main(int argc,char *argv[]){if(argv[1]==NULL||argv[2]==NULL){printf("请输入两个文件夹路径,第一个为源,第二个为目的!\n");exit(1);}char* source_path=argv[1];//取用户输入的第一个参数char* destination_path=argv[2];//取用户输入的第二个参数DIR* source=opendir(source_path);DIR* destination=opendir(destination_path);if(!source||!destination){printf("你输入的一个参数或者第二个参数不是文件夹!\n");}read_folder(source_path,destination_path);//进行文件夹的遍历/*线程并发开始*/pthread_mutex_init(&mutex,NULL);//初始化这个互斥锁  //声明并创建三个线程  pthread_t t1;  pthread_t t2;  pthread_t t3;  if(pthread_create(&t1,NULL,thread_function,"线程1")!=0){  printf("创建线程失败!程序结束!\n");  exit(1);  }  if(pthread_create(&t2,NULL,thread_function,"线程2")!=0){  printf("创建线程失败!程序结束!\n");  exit(1);  }  if(pthread_create(&t3,NULL,thread_function,"线程3")!=0){  printf("创建线程失败!程序结束!\n");  exit(1);  }  pthread_join(t1,NULL);  pthread_join(t2,NULL);  pthread_join(t3,NULL);  //三个线程都完成才能执行以下的代码  pthread_mutex_destroy(&mutex);//销毁这个互斥锁  /*线程并发结束*/return 0;}

这百来行代码,也没有什么好说的。

首先这个程序是《【Linux】线程互斥》(点击打开链接)与《【Linux】C语言实现文件夹拷贝》(点击打开链接)的结合体。里面涉及的概念,我已经在这两篇文件都详细写了,这里就不再大篇幅叙述了。

之后,由于涉及大量路径的拼接,搞到最后还是《【Linux】纯C环境下字符串的处理》(点击打开链接)问题,C语言就是这么烦,搞个字符串,要用到指针、数组、函数,等各种大量复杂的概念去处理。

3 0
原创粉丝点击