• 【Linux】线程并发拷贝程序


    据说大连某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;..;..)那个i
    int destination_arr_index=0;//存放目标文件路径的数组的索引
    pthread_mutex_t mutex;//声明一个相互排斥锁mutex
    int 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的长度+;
    	if(!result){//假设内存动态分配失败
    		printf("字符串连接时,内存动态分配失败
    ");
    		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_path
    	struct 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("源文件打开失败! "); exit(1); } if((out=fopen(destination_path,"w"))==NULL){//打开目标文件的文件流 printf("目标文件创建失败! "); 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的相互排斥锁创建失败。 ",(char *)arg); pthread_exit(NULL); } if(i<destination_arr_index){ copy_file(source_arr[i],destination_arr[i]);//复制单一文件 printf("%s复制%s到%s成功!

    ",(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("请输入两个目录路径,第一个为源。第二个为目的!

    "); 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("你输入的一个參数或者第二个參数不是目录! "); } 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("创建线程失败!程序结束!

    "); exit(1); } if(pthread_create(&t2,NULL,thread_function,"线程2")!=0){ printf("创建线程失败!

    程序结束。 "); exit(1); } if(pthread_create(&t3,NULL,thread_function,"线程3")!=0){ printf("创建线程失败!

    程序结束! "); 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语言就是这么烦,搞个字符串,要用到指针、数组、函数。等各种大量复杂的概念去处理。

  • 相关阅读:
    nmon系统监控
    Spring框架DataSource
    Spring框架的AOP
    jdk -version could not open jvm.cfg 的解决办法
    Spring框架annotation实现IOC介绍
    Junit4 单元测试框架的常用方法介绍
    页面静态化3 --- 使用PHP页面缓存机制来完成页面静态化(下)操作一个案例(新闻管理系统)
    页面静态化2 --- 使用PHP缓存机制来完成页面静态化(上)(ob_flush和flush函数区别用法)
    页面静态化1 --- 概念(Apache内置压力测试工具使用方法)
    memcached学习笔记6--浅谈memcached的机制 以及 memcached细节讨论
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7233938.html
Copyright © 2020-2023  润新知