多线程复制文件,同一文件下,多线程并发写入同一文件的不同部分。
思路是,提前为每个线程分配好写入内容大小,每个线程执行fopen获取单独的文件描述符,然后按分配的写入大小,fseek到不同的位置,并发写入内容。
但是写入的内容总是错乱。
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#define BUFF_SIZE 512
#define PTHREAD_NUMBER 4
typedef struct copy_block
{
char fin[BUFF_SIZE];
char fout[BUFF_SIZE];
long start; //起始位置
long segment_size; //分段大小
int id; //虚拟线程id
} __attribute__((packed)) page;
long file_size(char *filename)
{
struct stat fstat;
memset(&fstat, 0, sizeof(fstat));
stat(filename, &fstat);
return fstat.st_size;
}
//单线程任务逻辑
void pthread_copy(void *arg)
{
//转换指针类型
page *p = (page *)arg;
//每个线程单独打开文件
FILE *fin = fopen(p->fin, "r");
FILE *fout = fopen(p->fout, "wb+");
//移动流到偏移位置
int res1 = fseek(fin, p->start, SEEK_SET);
int res2 = fseek(fout, p->start, SEEK_SET);
//开始复制
char buffer[BUFF_SIZE]; //读写区
long read_size = BUFF_SIZE; //预设读写大小
long left = p->segment_size; //剩余大小,初始化为任务总大小
long reade_len = 0; //读取文件大小
long total_len = 0;
//当剩余大小大于0时保持复制
while (left > 0)
{
//如果文件剩余大小小于预设读写大小,则按剩余大小读取
if (read_size > left)
{
read_size = left;
}
//读取文件
reade_len = fread(buffer, 1, read_size, fin);
total_len += reade_len;
//写入文件
if (reade_len > 0)
{
fwrite(buffer, 1, reade_len, fout);
}
//剩余大小减去已读写大小
left = left - reade_len;
}
//复制完成关闭文件
fclose(fin);
fclose(fout);
pthread_exit(NULL);
}
//开启多线程任务
int multi_copy(char *src, char *dest)
{
//判断文件是否存在,以及是否具有读取权限
int file_exist = access(src, 4);
if (file_exist != 0)
fprintf(stderr, "源文件不存在");
//获取文件大小
long fsize = file_size(src);
//真正运行线程数量
int real_pthread_number = PTHREAD_NUMBER;
if (fsize < PTHREAD_NUMBER)
real_pthread_number = 1;
//给任务结构体分配内存
page *p;
p = malloc(sizeof(*p) * PTHREAD_NUMBER);
long offset = 0; //文本偏移量
long segment = fsize / real_pthread_number; //分段长度
long segment_remainder = fsize % real_pthread_number; //分段后剩余长度
//给每个线程分配任务
for (int i = 0; i < real_pthread_number; i++)
{
//分配复制任务的文件大小
if (i + 1 == real_pthread_number)
{
p[i].segment_size = segment + segment_remainder;
}
else
{
p[i].segment_size = segment;
}
//确定任务的起止位置
p[i].start = offset;
offset = offset + p[i].segment_size;
//文件路径存入任务
strncpy(p[i].fin, src, strlen(src));
strncpy(p[i].fout, dest, strlen(dest));
//分配虚拟线程id
p[i].id = i;
}
//创建线程
pthread_t work[real_pthread_number];
for (int i = 0; i < real_pthread_number; i++)
{
pthread_create(&work[i], NULL, (void *)&pthread_copy, (void *)&p[i]);
}
//阻塞主线程
for (int i = 0; i < real_pthread_number; i++)
{
pthread_join(work[i], NULL);
}
//释放任务结构体占用内存
if (p != NULL)
{
free(p);
p = NULL;
}
return 0;
}
int main(int argc, char *argv[])
{
char *src;
char *dest;
src = argv[1];
dest = argv[2];
multi_copy(src, dest);
}
在main中以rb打开源文件,wb+打开目标文件
修改结构体存储文件指针
在线程pthread_copy中操作文件指针位置,去除打开文件操作(在线程中以wb+打开文件会将文件截断为0)
在线程调度函数multi_copy中打开文件并在末尾关闭文件
将文件指针赋给每个线程的task_info结构体成员
二、线程锁版本