据说大连某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

#include

#include

#include//输出文件信息

#include//判断是否目录

#include//使用线程

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的长度+\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_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("源文件打开失败!\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

if(pthread_mutex_lock(&mutex)!=0){//对互斥锁上锁,临界区开始

printf("%s的互斥锁创建失败!\n",(char *)arg);

pthread_exit(NULL);

}

if(i

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

Linux 线程并发拷贝,【Linux】线程并发拷贝程序相关推荐

  1. Linux下套接字详解(七)----线程池accept处理高并发connect

    前言 服务器在调用listen和accept后,就会阻塞在accept函数上,accpet函数返回后循环调用accept函数等待客户的TCP连接. 我们知道服务器段listen套接字能处理的连接数与监 ...

  2. 深入剖析Linux IO原理和几种零拷贝机制的实现

    本文来说下Linux IO原理和几种零拷贝机制的实现 文章目录 概述 物理内存和虚拟内存 物理内存 虚拟内存 内核空间和用户空间 内核空间 用户空间 Linux的内部层级结构 Linux I/O读写方 ...

  3. Linux中的进程、线程和文件描述符

    说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案: 在 Linux 系统中,进程和线程几乎没有区别 . Linux 中的进程就是一个数据结构,看明白就可以理解文件描述符.重定向 ...

  4. 锁与并发工具包与线程池与LockSupport与Fork/Join框架与并行流串行流与阻塞队列与JPS,jstack命令查看死锁查看线程状态与AQS个人笔记九

    朝闻道,夕死可矣 本文共计 86564字,估计阅读时长1小时 点击进入->Thread源码万字逐行解析 文章目录 本文共计 86564字,估计阅读时长1小时 一锁 二Java中13个原子操作类 ...

  5. Java 多线程 并发 锁 Java线程面试题

    1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成 ...

  6. 【Linux】C++项目实战-高并发服务器详析

    目录 多进程实现并发服务器 多线程实现并发服务器 BIO模型 NIO模型 I/O多路复用(I/O多路转接) select 主旨思想 图解原理 函数解析 代码举例 select的缺点 poll 函数解析 ...

  7. mongodb线程池_常用高并发网络线程模型设计及MongoDB线程模型优化实践

    服务端通常需要支持高并发业务访问,如何设计优秀的服务端网络IO工作线程/进程模型对业务的高并发访问需求起着至关重要的核心作用. 本文总结了了不同场景下的多种网络IO线程/进程模型,并给出了各种模型的优 ...

  8. 并发--基本的线程机制

    1. 基本概念 线程(英语:thread)是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线 ...

  9. Linux 操作系统原理 — 进程与线程管理

    目录 文章目录 目录 前言 进程与线程 内核线程,用户线程与轻量级进程 内核线程 轻量级进程 用户线程 轻量级进程与用户线程的区别 用户线程与轻量级进程的混合模式 用户线程和内核线程的区别 线程的实现 ...

  10. 【Java 并发编程】线程简介 ( 并发类型 | 线程状态 | CPU 数据缓存 )

    文章目录 一.并发类型 二.线程状态 三.CPU 数据缓存 一.并发类型 并发类型 : Thread Runnable Future ThreadPool 其中 Runnable , ThreadPo ...

最新文章

  1. Linux多线程同步------条件变量
  2. Java基础学习总结(52)——Liunx系统Centos上搭建Java开发环境
  3. 如何在Java中选择Map/List/Set
  4. lisp实战文库_LISP编程举例
  5. 玻璃体液化研究(控制)
  6. 怎样下载安装python_Windows系统上如何安装Python和pip
  7. php 顺序结构,顺序存储结构php实现
  8. 百度论文引用网络节点分类比赛
  9. 史上最大漏洞危机再生新变种,大量芯片受感染
  10. linux-网络数据包抓取-tcpdump
  11. iOS-----------关于UDID
  12. 全球前十大证券交易所在区块链领域的探索和布局
  13. 33个网站足以使你成为一个天才
  14. 愿你与生活交手多年,依然满怀新鲜,
  15. 桌面支持--ESP分区和MSR分区下怎么做系统~~
  16. 验房师去哪找靠谱,验房项目以及验房整改建议,精装房验房项目、毛坯房验房项目
  17. HTML中placeholder用法
  18. 触动传媒总部人去楼空 上海多家出租车公司追讨欠款
  19. python周末吃什么_中午不知道吃什么,用Python爬取美团外卖评论帮你选餐
  20. 北京内推 | ​微软亚洲研究院DKI组招聘数学规划方向实习生

热门文章

  1. 指静脉识别技术在安防行业的应用前景
  2. leetCode 206. Reverse Linked List 反转链表
  3. JVM并发机制探讨—内存模型、内存可见性和指令重排序
  4. XNA中三维空间物体的几种旋转方法
  5. C语言为何到现在都是热门语言?原因很简单,程序员大牛都知道
  6. 2019年最佳Python学习路线
  7. AI来袭程序猿是否该学习Python了
  8. 老码农的人生需要多线程的去生活
  9. 今天的男生代表甲方的飞鸽传书
  10. 评论:北京“网店新规”将死在问题中