You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C++多线程文件复制程序仅生成空文件问题求助

问题根因&修复点

  • sendfile参数顺序写反:Linux系统sendfile的参数顺序是sendfile(输出文件描述符, 输入文件描述符, 偏移量指针, 传输长度),你把读写fd传反了,自然没有数据写入目标文件。
  • 偏移量完全未正确计算:PART结构体里的offset没有赋值,你还用块大小当偏移量定位写入位置,直接跳过了文件开头的内容,且offset用了指针类型未分配内存,传空指针会导致调用失败。
  • 统计文件大小的时机错误:你在调用stat(file, &f_stat)之前就用f_stat.st_size计算分块大小,拿到的是未初始化的垃圾值,分块逻辑完全无效。
  • 变量名错误:循环条件里写的number_threads不存在,你实际定义的变量名是threads_number,编译都过不了。
  • 线程串行执行:你创建线程后立刻调用pthread_join,等于前一个线程执行完才会创建下一个,完全没用到多线程并行的能力。
  • 全局文件描述符线程不安全:多个线程同时改写全局的fin、fout变量会导致数据竞争,且每次打开文件后未关闭,存在资源泄漏,main函数里打开的fin1、fout1也完全没有用到。
  • 输出路径处理错误:你的参数第二个是目标目录,你直接把目录路径当作文件打开会失败,需要拼接源文件名到目标目录下生成完整的输出文件路径。

修复后完整代码

/*Multithreads file copier*/
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <string.h>
#include <libgen.h>

const char* file;
char fileout_path[PATH_MAX];

// 每个线程处理的分块信息
struct PART{
    off_t offset; // 改为非指针类型,存储当前分块的起始偏移
    size_t size;
};

void *Copy (void *data)
{
    struct PART *mypart = (struct PART *) data;
    // 每个线程独立打开文件,避免共享fd的偏移竞争
    int fin = open(file, O_RDONLY, 0);
    int fout = open(fileout_path, O_WRONLY, 0644);
    if (fin < 0 || fout < 0) {
        perror("open file failed");
        pthread_exit(NULL);
    }
    // 定位输出文件的写入位置
    lseek(fout, mypart->offset, SEEK_SET);
    off_t offset = mypart->offset;
    sendfile(fout, fin, &offset, mypart->size);
    printf("thread处理块:偏移%ld,大小%ld\n", mypart->offset, mypart->size);
    close(fin);
    close(fout);
    pthread_exit(0);
}

int main(int argc, char *argv[])
{
    if (argc < 3) {
        printf("用法:./a.out <源文件路径> <目标目录> <线程数(默认4)>\n");
        return 1;
    }
    int threads_number = 4;
    if (argv[3] != NULL) {
        threads_number = atoi(argv[3]);
        if (threads_number <= 0) threads_number = 4;
    }

    pthread_t tid[threads_number];
    struct stat f_stat;
    struct PART part[threads_number];
    
    file = argv[1];
    // 先获取源文件大小再计算分块
    if (stat(file, &f_stat) < 0) {
        perror("获取源文件信息失败");
        return 1;
    }
    // 拼接目标文件完整路径:目标目录+源文件名
    char* filename = basename((char*)file);
    snprintf(fileout_path, sizeof(fileout_path), "%s/%s", argv[2], filename);
    // 预创建目标文件并调整大小,避免多线程写入时文件扩展竞争
    int fout_tmp = open(fileout_path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    if (fout_tmp < 0) {
        perror("创建目标文件失败");
        return 1;
    }
    ftruncate(fout_tmp, f_stat.st_size);
    close(fout_tmp);

    // 计算每个线程的分块偏移和大小
    unsigned long part_size = f_stat.st_size / threads_number;
    for(int i =0; i < threads_number; i++) {
        part[i].offset = i * part_size;
        if (i == threads_number -1) {
            part[i].size = f_stat.st_size - part[i].offset;
        } else {
            part[i].size = part_size;
        }
    }

    // 先创建所有线程,再统一等待执行完成,实现并行
    for (int j = 0; j < threads_number; j++) {
        pthread_create(&tid[j], NULL, Copy, (void *)&part[j]);
    }
    for (int j = 0; j < threads_number; j++) {
        pthread_join(tid[j], NULL);
    }

    printf("所有线程执行完成,文件复制成功\n");
    return 0;
}

编译运行方法

编译需要链接pthread库:
g++ copy.cpp -o copy -lpthread
运行命令示例:
./copy test.txt /tmp 8

内容的提问来源于stack exchange,提问作者Zahra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 15:45:02