Linux 线程并发拷贝,【Linux】线程并发拷贝程序
據(jù)說(shuō)大連某211高校的李教授越來(lái)越重口,不僅延續(xù)要求他所帶的每一個(gè)本科班,都要寫(xiě)一份線程并發(fā)拷貝程序的傳統(tǒng),而且還開(kāi)始規(guī)定不能用Java語(yǔ)言寫(xiě)作,導(dǎo)致我之前寫(xiě)的《【Java】線程并發(fā)拷貝程序》(點(diǎn)擊打開(kāi)鏈接)作廢。所有李教授旗下的學(xué)生,必須在毫無(wú)圖形界面的Linux系統(tǒng),用里面vi去寫(xiě)作。這更讓莘莘學(xué)子們感覺(jué)本來(lái)頭來(lái)就不光明的天空更加黑暗起來(lái)。
更重要的是,若干年過(guò)去了,網(wǎng)上對(duì)其的研究與資料,依舊是少數(shù),依舊是那份流傳已久,以訛傳訛的C語(yǔ)言版。
雖然這個(gè)程序毫無(wú)研究?jī)r(jià)值,但是本著治病救人,同時(shí)借此深入研究LinuxC的線程編程機(jī)制,我再一次完成了這份,在Linux用最最最純種C語(yǔ)言完成的線程并發(fā)拷貝程序。
如下圖,搞了3個(gè)線程在Linux系統(tǒng)下完成文件夾A中的所有內(nèi)容到空空是也文件夾B的復(fù)制。同時(shí)按照李教授的喜好,在前面補(bǔ)個(gè)前綴,也就是重命名目標(biāo)文件夾。
可能有些同學(xué)說(shuō)我上面的圖形化的Linux系統(tǒng)與無(wú)圖形化的Linux是不同的。
其實(shí)上述代碼在各個(gè)版本的Linux系統(tǒng)都可以運(yùn)行的,只是你寫(xiě)C語(yǔ)言要用《【Linux】vi/vim的使用》(點(diǎn)擊打開(kāi)鏈接)去寫(xiě),查詢文件與文件夾,要用cd命令先進(jìn)入相關(guān)的路徑,同時(shí)用dir命令讀這個(gè)文件夾目錄。要是你覺(jué)得vi難用,可以用《【Linux】用Winscp遠(yuǎn)程訪問(wèn)無(wú)圖形界面的Linux系統(tǒng)》(點(diǎn)擊打開(kāi)鏈接),將你在windows下寫(xiě)好的代碼,傳上Linux,再用gcc編譯再運(yùn)行。
具體代碼如下:
#include
#include
#include
#include//輸出文件信息
#include//判斷是否目錄
#include//使用線程
char *source_arr[512];//存放源文件路徑的數(shù)組
char *destination_arr[512];//存放目標(biāo)文件路徑的數(shù)組
int source_arr_index=0;//存放源文件路徑的數(shù)組的索引,就是for(int i=xx;..;..)那個(gè)i
int destination_arr_index=0;//存放目標(biāo)文件路徑的數(shù)組的索引
pthread_mutex_t mutex;//聲明一個(gè)互斥鎖mutex
int i=0;//多個(gè)線程函數(shù)用到這個(gè)i,用于記錄是否復(fù)制完畢,因此作為全局變量處理~
/*字符串處理函數(shù)*/
int endwith(char* s,char c){//用于判斷字符串結(jié)尾是否為“/”與“.”
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的長(zhǎng)度+str2的長(zhǎng)度+\0;
if(!result){//如果內(nèi)存動(dòng)態(tài)分配失敗
printf("字符串連接時(shí),內(nèi)存動(dòng)態(tài)分配失敗\n");
exit(1);
}
strcat(result,str1);
strcat(result,str2);//字符串拼接
return result;
}
/*遍歷函數(shù)*/
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){//復(fù)制文件夾
if(!opendir(destination_path)){
if (mkdir(destination_path,0777))//如果不存在就用mkdir函數(shù)來(lái)創(chuàng)建
{
printf("創(chuàng)建文件夾失敗!");
}
}
char *path;
path=(char*)malloc(512);//相當(dāng)于其它語(yǔ)言的String path="",純C環(huán)境下的字符串必須自己管理大小,這里為path直接申請(qǐng)512的位置的空間,用于目錄的拼接
path=str_contact(path,source_path);//這三句,相當(dāng)于path=source_path
struct dirent* filename;
DIR* dp=opendir(path);//用DIR指針指向這個(gè)文件夾
while(filename=readdir(dp)){//遍歷DIR指針指向的文件夾,也就是文件數(shù)組。
memset(path,0,sizeof(path));
path=str_contact(path,source_path);
//如果source_path,destination_path以路徑分隔符結(jié)尾,那么source_path/,destination_path/直接作路徑即可
//否則要在source_path,destination_path后面補(bǔ)個(gè)路徑分隔符再加文件名,誰(shuí)知道你傳遞過(guò)來(lái)的參數(shù)是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,"/");
}
//取文件名與當(dāng)前文件夾拼接成一個(gè)完整的路徑
file_source_path=str_contact(file_source_path,filename->d_name);
if(is_dir(file_source_path)){//如果是目錄
if(!endwith(file_source_path,'.')){//同時(shí)并不以.結(jié)尾,因?yàn)長(zhǎng)inux在所有文件夾都有一個(gè).文件夾用于連接上一級(jí)目錄,必須剔除,否則進(jìn)行遞歸的話,后果無(wú)法想象!
file_destination_path=str_contact(file_destination_path,filename->d_name);//對(duì)目標(biāo)文件夾的處理,取文件名與當(dāng)前文件夾拼接成一個(gè)完整的路徑
read_folder(file_source_path,file_destination_path);//進(jìn)行遞歸調(diào)用,相當(dāng)于進(jìn)入這個(gè)文件夾進(jìn)行遍歷~
}
}
else{//否則,將源文件于目標(biāo)文件的路徑分別存入相關(guān)數(shù)組
//對(duì)目標(biāo)文件夾的處理,取文件名與當(dāng)前文件夾拼接成一個(gè)完整的路徑
file_destination_path=str_contact(file_destination_path,"前綴_");//給目標(biāo)文件重命名,這里示意如何加個(gè)前綴~^_^
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++;
}
}
}
/*復(fù)制函數(shù)*/
void copy_file(char* source_path,char *destination_path){//復(fù)制文件
char buffer[1024];
FILE *in,*out;//定義兩個(gè)文件流,分別用于文件的讀取和寫(xiě)入int len;
if((in=fopen(source_path,"r"))==NULL){//打開(kāi)源文件的文件流
printf("源文件打開(kāi)失敗!\n");
exit(1);
}
if((out=fopen(destination_path,"w"))==NULL){//打開(kāi)目標(biāo)文件的文件流
printf("目標(biāo)文件創(chuàng)建失敗!\n");
exit(1);
}
int len;//len為fread讀到的字節(jié)長(zhǎng)
while((len=fread(buffer,1,1024,in))>0){//從源文件中讀取數(shù)據(jù)并放到緩沖區(qū)中,第二個(gè)參數(shù)1也可以寫(xiě)成sizeof(char)
fwrite(buffer,1,len,out);//將緩沖區(qū)的數(shù)據(jù)寫(xiě)到目標(biāo)文件中
}
fclose(out);
fclose(in);
}
/*線程執(zhí)行函數(shù)*/
void *thread_function(void *arg){
while(i
if(pthread_mutex_lock(&mutex)!=0){//對(duì)互斥鎖上鎖,臨界區(qū)開(kāi)始
printf("%s的互斥鎖創(chuàng)建失敗!\n",(char *)arg);
pthread_exit(NULL);
}
if(i
copy_file(source_arr[i],destination_arr[i]);//復(fù)制單一文件
printf("%s復(fù)制%s到%s成功!\n",(char *)arg,source_arr[i],destination_arr[i]);
i++;
sleep(1);//該線程掛起1秒
}
else{//否則退出
pthread_exit(NULL);//退出線程
}
pthread_mutex_unlock(&mutex);//解鎖,臨界區(qū)結(jié)束
sleep(1);//該線程掛起1秒
}
pthread_exit(NULL);//退出線程
}
/*主函數(shù)*/
int main(int argc,char *argv[]){
if(argv[1]==NULL||argv[2]==NULL){
printf("請(qǐng)輸入兩個(gè)文件夾路徑,第一個(gè)為源,第二個(gè)為目的!\n");
exit(1);
}
char* source_path=argv[1];//取用戶輸入的第一個(gè)參數(shù)
char* destination_path=argv[2];//取用戶輸入的第二個(gè)參數(shù)
DIR* source=opendir(source_path);
DIR* destination=opendir(destination_path);
if(!source||!destination){
printf("你輸入的一個(gè)參數(shù)或者第二個(gè)參數(shù)不是文件夾!\n");
}
read_folder(source_path,destination_path);//進(jìn)行文件夾的遍歷
/*線程并發(fā)開(kāi)始*/
pthread_mutex_init(&mutex,NULL);//初始化這個(gè)互斥鎖
//聲明并創(chuàng)建三個(gè)線程
pthread_t t1;
pthread_t t2;
pthread_t t3;
if(pthread_create(&t1,NULL,thread_function,"線程1")!=0){
printf("創(chuàng)建線程失敗!程序結(jié)束!\n");
exit(1);
}
if(pthread_create(&t2,NULL,thread_function,"線程2")!=0){
printf("創(chuàng)建線程失敗!程序結(jié)束!\n");
exit(1);
}
if(pthread_create(&t3,NULL,thread_function,"線程3")!=0){
printf("創(chuàng)建線程失敗!程序結(jié)束!\n");
exit(1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
//三個(gè)線程都完成才能執(zhí)行以下的代碼
pthread_mutex_destroy(&mutex);//銷毀這個(gè)互斥鎖
/*線程并發(fā)結(jié)束*/
return 0;
}
這百來(lái)行代碼,也沒(méi)有什么好說(shuō)的。
首先這個(gè)程序是《【Linux】線程互斥》(點(diǎn)擊打開(kāi)鏈接)與《【Linux】C語(yǔ)言實(shí)現(xiàn)文件夾拷貝》(點(diǎn)擊打開(kāi)鏈接)的結(jié)合體。里面涉及的概念,我已經(jīng)在這兩篇文件都詳細(xì)寫(xiě)了,這里就不再大篇幅敘述了。
之后,由于涉及大量路徑的拼接,搞到最后還是《【Linux】純C環(huán)境下字符串的處理》(點(diǎn)擊打開(kāi)鏈接)問(wèn)題,C語(yǔ)言就是這么煩,搞個(gè)字符串,要用到指針、數(shù)組、函數(shù),等各種大量復(fù)雜的概念去處理。
總結(jié)
以上是生活随笔為你收集整理的Linux 线程并发拷贝,【Linux】线程并发拷贝程序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 命令及串口命令_嵌入式Linux系列第7
- 下一篇: linux禁止防火墙命令,LINUX关闭