多线程文件复制C++

前言

  前一段学习Linux编程的时候准备了一些小程序来练手,今天写到了多线程文件拷问,遇到了一些问题,总结一下。

程序功能

  启动程序输入源文件名和目的文件名,可以将源文件内容复制一份到目的文件。

程序分析

  1. 打开源文件,获取(fstat)源文件大小
  2. 打开(创建)目的文件,修改(ftruncate)目的文件的大小
  3. 创建线程,进行拷贝,对文件大小分解,每个线程只负责拷贝部分文件,拷贝大小通过len/MAX_PTHREAD计算,对最后一个进程多拷贝len%MAX_PTHREAD个字节。采用 fseek移动文件偏移量,nleft确定需要拷贝的字节的大小。

遇到的问题

  1. 写入的大小不能用strlen控制:因为读取的数据内有’\0’数据,strlen遇到’\0’停止计算长度,导致fwrite写入的数据小于fread读到的输入。
  2. fread读取的文件偏移量问题:一个文件指针有一个偏移量,在线程中共享,为了避免当前线程的文件偏移量不会被其他线程修改,需要对文件偏移量加锁,对文件加锁即对文件偏移量加锁。因为线程的主要任务就是根据文件的偏移量对文件进行读写,如果对其加锁就没有多线程的意义了,这里采用每个线程对文件打开一次,修改文件偏移量,保证当前线程的文件偏移量不会被其他线程修改。

程序测试

  因为最初写这个程序的时候读写是使用的文件描述符,对文件读写时的文件偏移量无法有效控制,导致运行程序不稳定,有时正确,有时错误。后来改成文件指针的形式,随便测了几次,都正确了,想确定是否真的正确,我写了一个守护进程每隔一分钟测一次,经过一晚上的测试,结果完全正确,截图如下:
在这里插入图片描述在这里插入图片描述

程序源码

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<cstdlib>

#define MAX_PTHREAD 100

using namespace std;

struct Thread_information
{
        char* src;
        char* dst;
        int ith;
};
int len;//文件的总大小
void sys_err(const char* err)
{
        perror(err);
        exit(1);
}

void *copy(void *arg)
{
        Thread_information p = *((Thread_information*)arg);
        delete (Thread_information *)arg;
        int nleft = len/MAX_PTHREAD;
        if(p.ith+1==MAX_PTHREAD)
        {
                nleft += len%MAX_PTHREAD;
        }
        FILE * SRC = fopen(p.src,"r");
        FILE *DST = fopen(p.dst,"w");
        int ret = fseek(SRC,p.ith*(len/MAX_PTHREAD),SEEK_SET);
        if(ret == -1)
                sys_err("fseek error");
        ret = fseek(DST,p.ith*(len/MAX_PTHREAD),SEEK_SET);
        if(ret == -1)
                sys_err("fseek error");

        int nread;
        char buf[1024];
//      printf("%d %d %d\n",p.ith,(int)srcfp,(int)dstfp);
//      fflush(stdout);
        while(nleft>0)
        {

                if(nleft>=1024)
                        nread = fread(buf,1,sizeof(buf),SRC);
                else
                        nread = fread(buf,1,nleft,SRC);
        //      if(nread==0)
        //              printf("%d : read %d nleft %d curseek %d\n",p.ith,nread, nleft,(int)lseek(p.srcfd,0,SEEK_CUR));
                nread = fwrite(buf,1,nread,DST);
                nleft -= nread;
        }
        fclose(SRC);
        fclose(DST);
        printf("-");
        fflush(stdout);
}

int main(int argc, char *argv[])
{
        Thread_information *information = new Thread_information;
        if(argc!=3)
        {
                printf("usage: %s <srcfile> <dstfile>\n",argv[0]);
        }
        int srcfd = open(argv[1],O_RDONLY);
        if(srcfd==-1)
                sys_err("open srcfile error");
        int dstfd = open(argv[2],O_WRONLY|O_CREAT,0664);
        if(dstfd==-1)
                sys_err("open dstfile error");
        struct stat stcur;
        int ret = fstat(srcfd,&stcur);
        if(ret==-1)
                sys_err("fstat error");
        len = stcur.st_size;
        ret = ftruncate(dstfd,len);
        if(ret==-1)
                sys_err("ftruncate error");
        close(srcfd);
        close(dstfd);
        pthread_t pid[MAX_PTHREAD];
        for(int i = 0; i< MAX_PTHREAD; i++)
        {
                information = new Thread_information;
                information->ith = i;
                information->src = argv[1];
                information->dst = argv[2];
                if(pthread_create(&pid[i],NULL,copy,(void *)information)!=0)
                        perror("create");
        }
        for(int i = 0; i < MAX_PTHREAD; i++)
                pthread_join(pid[i],NULL);
        printf("\n");
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值