问题的背景: 我有一个大型二进制文件,其中包含具有独特结构的数据。该数据的单位称为“事件”。每个事件有 32016 字节,单个文件包含约 400000 个事件,使文件约 12 GB。我正在编写一个程序来处理事件,并尝试使用多线程方法,其中多个线程读取文件的不同段(让每个线程使用自己的文件流)。
问题是 fseek 无法查找到文件的正确位置。以下是最小的可重现示例。该程序读取包含 473797 个事件的二进制文件,并计划使用 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,而有时会失败并返回错误代码 22(无效参数)。
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
,它显然是 4 个字节宽。对于此宽度,INT_MAX
是 2147483647。
让我们看看: | k |
eventsPerThread * eventSize * 4 * k
|溢出了int
| fseek()
| 的返回值
|-|-|-|-|
| 0 | 0 | 0 | 0 |
| 1 | 758427024 | 758427024 | 0 |
| 2 | 1516854048 | 1516854048 | 0 |
| 3 | 2275281072 | -2019686224 | -1 |
| 4 | 3033708096 | -1261259200 | -1 |
| 5 | 3792135120 | -502832176 | -1 |
| 6 | 4550562144 | 255594848 | 0 |
| 7 | 5308989168 | 1014021872 | 0 |
| : | : | : | : |
由于溢出,结果
int
变为负值,fseek()
对此并不满意。
首先,确保您的
long
的宽度超过 4 个字节。然后将乘法的至少一个操作数更改为 long
。例如这样eventsPerThread * eventSize * 4L * k
.
最后一点:考虑使用更多空格以使代码更具可读性。