我们是否需要互斥锁来执行多线程文件IO

时间:2018-03-20 06:15:38

标签: c++ pthreads mutex fseek fputs

我正在尝试使用多个线程(pthread)对文件进行随机写入(Benchmark测试)。看起来如果我注释掉mutex lock,则创建的文件大小小于实际值,好像有些写入丢失了(总是在块大小的多个中)。但是,如果我保留互斥锁,它总是精确的大小。

我的代码是否在其他地方出现问题,并且实际上不需要互斥锁(@evan as suggested)或者必须使用互斥锁

void *DiskWorker(void *threadarg) {

FILE *theFile = fopen(fileToWrite, "a+");
....
for (long i = 0; i < noOfWrites; ++i) {
            //pthread_mutex_lock (&mutexsum);
            // For Random access

            fseek ( theFile , randomArray[i] * chunkSize  , SEEK_SET );
            fputs ( data , theFile );

            //Or for sequential access (in this case above 2 lines would not be here)

            fprintf(theFile, "%s", data);
            //sequential access end

            fflush (theFile);
            //pthread_mutex_unlock(&mutexsum);
        }
.....
}

2 个答案:

答案 0 :(得分:2)

您正在使用&#34;追加模式&#34;打开文件。根据C11:

  

打开一个附加模式的文件('a'作为第一个字符   mode argument)导致强制所有后续写入文件   到当时的当前文件结束,无论是否进行干预   fseek函数。

C标准没有规定应该如何实现这一点,但是在POSIX系统上,这通常使用open函数的O_APPEND标志实现,而使用函数write完成刷新数据。请注意,代码中的fseek调用应该无效。

我认为POSIX需要这个,因为它描述了如何在追加模式(>>)中重定向输出the shell

  

附加输出重定向将导致名称结果的文件   从扩展单词到指定的输出开放   文件描述符。该文件被打开,就好像open()函数一样   调用POSIX.1-2008的系统接口卷中定义的   使用O_APPEND标志。如果该文件不存在,则应该是   创建

由于大多数程序都使用FILE界面将数据发送到stdout,这可能要求 fopen使用open O_APPEND编写数据时,和write(而不是像pwrite这样的函数)。

因此,如果您的系统fopen使用'a'模式使用O_APPEND并使用write进行刷新,并且您的内核和文件系统正确实现O_APPEND标记,写入do not intervene时,使用互斥锁应该没有效果:

  

如果设置了文件状态标志的O_APPEND标志,则为该文件   offset应在每次写入之前设置为文件的末尾,否则   干预文件修改操作应在更改之间进行   文件偏移和写操作。

请注意,并非所有文件系统都支持此行为。查看this回答。

至于我对你上一个问题的回答,我的建议是删除互斥锁,因为它应该对文件的大小没有影响(并且它对我的机器没有任何影响)。

就个人而言,我从未真正使用过O_APPEND而且会犹豫不决,因为它的行为在某种程度上可能不受支持,而且它在Linux上的行为很奇怪(参见&#34; bug&#34; pwrite)部分。

答案 1 :(得分:1)

您肯定需要一个互斥锁,因为您发出了几个不同的文件命令。底层文件子系统可能无法知道您要调用多少文件命令来完成整个操作。

所以你需要互斥锁。

在您的情况下,您可能会发现将mutex 置于循环之后会获得更好的性能。原因在于,否则,线程之间的切换可能导致磁盘的不同部分之间的过度跳跃。硬盘需要大约10ms才能移动读/写头,这样可能会减少很多东西。

因此,对此进行基准测试可能是一个好主意。