【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的长度+\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<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语言就是这么烦,搞个字符串,要用到指针、数组、函数。等各种大量复杂的概念去处理。
【Linux】线程并发拷贝程序的更多相关文章
- Linux Epoll介绍和程序实例
Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...
- Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL
Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...
- Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL
Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...
- [转]Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL
转载地址:https://www.cnblogs.com/MYSQLZOUQI/p/4233630.html 自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱 ...
- [转载]Linux 线程实现机制分析
本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...
- linux线程的实现
首先从OS设计原理上阐明三种线程:内核线程.轻量级进程.用户线程 内核线程 内核线程就是内核的分身,一个分身可以处理一件特定事情.这在处理异步事件如异步IO时特别有用.内核线程的使用是廉价的,唯一使用 ...
- linux线程的实现【转】
转自:http://www.cnblogs.com/zhaoyl/p/3620204.html 首先从OS设计原理上阐明三种线程:内核线程.轻量级进程.用户线程 内核线程 内核线程就是内核的分身,一个 ...
- Linux线程学习(一)
一.Linux进程与线程概述 进程与线程 为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,线程共享相同的内存空间.不同的线程可以存取内存中的同一个变量.所以,程序中的所有线程都可 ...
- Linux线程学习(二)
线程基础 进程 系统中程序执行和资源分配的基本单位 每个进程有自己的数据段.代码段和堆栈段 在进行切换时需要有比较复杂的上下文切换 线程 减少处理机的空转时间,支持多处理器以及减少上下文切换开销, ...
随机推荐
- 关于设置组件的state时遇到的一些问题
在使用react-bootstrap的时候设置showModel的值来控制Model的显示与隐藏,但是setState这个函数是异步的. 当你进行数据更新的时候,接着执行函数获取这个模态框的dom是获 ...
- 将npm改成默认使用cnpm下载
淘宝的cnpm下载安装的命令为 npm install -g cnpm --registry=https://registry.npm.taobao.org 但是仅仅这样是不够的,这样只有主动去下载资 ...
- IntelliJ IDEA如何导入Gradle项目
最近学习Gradle,本文来重点介绍一下,如何使用IntelliJ IDEA导入`Gradle项目的一种方法. 选择菜单栏的File->Open. 在打开的窗口选择对应Gradle项目的buil ...
- JavaScript中in的用法
JavaScript中的in 操作符是对Object(对象)操作的,并不是针对数组. in 的右边必须是对象变量 例如:var data = {id:1, name:'AAA'};if('name' ...
- Google 最新的 Fuchsia OS【科技讯息摘要】
转自:http://www.cnblogs.com/pied/p/5771782.html 就是看到篇报道,有点好奇,就去FQ挖了点东西回来. 我似乎已开始就抓到了重点,没错,就是 LK . LK 是 ...
- compensation 在 spec 上的意義
compensation 翻譯為補償, 之前觀念都認為補償都是正的, 原來補償也可以負的, 例子: 電池充電電流在 10 - 45 度為 1800 mA, 在 0 - 10 度時,jeita 補償 1 ...
- XPath语法 在C#中使用XPath例子与用法
XPath可以快速定位到Xml中的节点或者属性.XPath语法很简单,但是强大够用,它也是使用xslt的基础知识.示例Xml: <?xml version="1.0" enc ...
- 查看windows进程,并删除
1. 通过[任务管理器]可以查看windows进程. 有些进程不在[任务管理器]中. 2. 通过tasklist命令查看进程. 杀掉进程: epmd 进程,在停止.卸载后rabbitmq服务还在. 通 ...
- poj 2100(尺取法)
Graveyard Design Time Limit: 10000MS Memory Limit: 64000K Total Submissions: 6107 Accepted: 1444 ...
- HDU 1022 Train Problem I[给出两个长n的串,入栈和出栈顺序,判断入栈顺序是否可以匹配出栈顺序]
Train Problem I 时间限制:3000 ms | 内存限制:65535 KB 难度:2 描述 As the new term comes, the Ignatius Train Sta ...