多线程场景下用多文件流读取文件时fseek失败的原因
问题
问题背景
我有一个结构独特的大型二进制文件,其中的数据单元称为“event”。每个event占32016字节,单个文件约包含400000个event,文件大小约12GB。我正在编写程序处理这些event,尝试采用多线程方案,让每个线程使用独立的文件流读取文件的不同段。
问题现象
fseek函数无法定位到文件的正确位置。以下是最小复现代码(当前为单线程,后续会扩展为多线程),程序读取包含473797个event的二进制文件,计划使用20个线程,每个线程对应一个独立文件流:
#include <iostream> #include <stream> #include <errno.h> #include <string.h> using namespace std; int main(){ FILE *segment[20]; int ret=0; int eventsPerThread=473797/20; int eventSize=8004; for(int k=0;k<20;++k){ segment[k]=fopen("Datafile_367.bin","rb"); if(segment[k]==NULL){ std::cout<<"file stream is NULL!"<<k<<"\n"; } ret=fseek(segment[k],eventsPerThread*eventSize*4*k,SEEK_SET); std::cout<<ret<<":::"<<strerror(errno)<<"\n"; } return 0; }
程序输出如下,fseek有时成功返回0,有时失败返回-1,错误码为22(Invalid argument):
0:::Success 0:::Success 0:::Success -1:::Invalid argument -1:::Invalid argument -1:::Invalid argument 0:::Invalid argument 0:::Invalid argument 0:::Invalid argument -1:::Invalid argument -1:::Invalid argument -1:::Invalid argument 0:::Invalid argument 0:::Invalid argument 0:::Invalid argument -1:::Invalid argument -1:::Invalid argument 0:::Invalid argument 0:::Invalid argument 0:::Invalid argument
请问出现这种fseek函数行为的原因是什么?
原因分析
你的问题出在偏移量计算时的整数溢出:
- 你用
int类型存储偏移量的计算结果,但eventsPerThread * eventSize * 4 * k的数值会远超过32位int的最大值(2147483647)。比如当k=19时,计算结果为23689 * 8004 * 4 * 19 ≈ 14.3GB,这个数值远大于int能容纳的范围,会触发整数溢出,得到一个无效的负数或乱码值。 fseek的第二个参数offset在标准C中是long类型(64位系统下通常为64位,32位系统下为32位),当你传入溢出后的无效int值时,就会触发"Invalid argument"错误。
修复方法
- 将偏移量计算的变量改为
long long类型,确保计算过程中不会溢出:long long offset = (long long)eventsPerThread * eventSize * 4 * k; ret = fseek(segment[k], offset, SEEK_SET); - 同时建议将
eventsPerThread、eventSize这类变量改用更大的整数类型(比如long)存储,避免中间计算步骤发生溢出。
另外注意代码中的一个小错误:头文件应该是<fstream>而非<stream>,不过这不是fseek失败的直接原因。
内容的提问来源于stack exchange,提问作者danula godagama
相关产品推荐
相关产品推荐

