我们有一个C ++类,它基本上从二进制文件中读取和写入向量。将单个向量加载到内存中的示例性读取函数如下所示:
int load (const __int64 index, T* values) const {
int re = _fseeki64(_file, index * _vectorSize + _offsetData, SEEK_SET);
assert(re == 0);
size_t read = fread(values, sizeof(T), _vectorElements, _file);
assert(read == _vectorElements);
return 0;}
Out程序是多线程的,使用OpenMP,多个线程同时访问同一个文件。为了避免因多线程而导致的问题,我们总是在OpenMP关键语句中覆盖函数调用:
#pragma omp critical {
load(...);
}
我知道Microsoft Visual C ++运行时包含多个函数,如_fseek_nolock
,_fread_nolock
,_fwrite_nolock
等等...例如,_fread_nolock()
函数被描述为
此功能是fread的非锁定版本。它与fread完全相同,只是它不受其他线程的干扰。它可能更快,因为它不会导致锁定其他线程的开销。仅在线程安全的上下文中使用此函数,例如单线程应用程序或调用作用域已处理线程隔离。
现在我的问题:我理解该函数会阻止“重入”调用,因此在其他线程返回之前,没有其他线程会进入该函数。但是,我不明白为什么有必要以这种方式保护单个功能。恕我直言,所有访问/修改文件指针(代码示例中的_file
)的函数必须受到保护,因此必须是线程安全的。这需要在整个功能块周围建立一个锁定,实现调用标准C函数fseek和fread,所以我没有看到提供这种非阻塞函数的意义。
有人可以向我解释这些锁定机制,因为我认为我们的偏执锁定方案会浪费一些性能吗?
提前谢谢!
答案 0 :(得分:1)
如果您使用Microsoft多线程C运行时,所有需要全局或静态变量的函数都可以正常工作(例如printf和fread,不要问我为什么它们需要全局变量)。但是,您仍然无法将FILE *结构传递给写入它的函数,并期望它是线程安全的。
因此,微软的“线程安全”函数只有在它们是可重入的意义上才是线程安全的,即对全局和静态的所有访问都是通过互斥或类似的方式完成的。但是在某种意义上你不能同时使用相同的FILE *调用两个fprintf()。
来源:http://msdn.microsoft.com/en-us/library/1bh5ewb2%28VS.71%29.aspx
答案 1 :(得分:1)
对于一些简单的代码,FILE *中的锁定就足够了。考虑一个基本的日志记录基础结构,您希望所有线程都通过公共文件*进行日志记录。内部锁定将确保FILE *不会被多个线程损坏,并且由于每个日志行应该是独立的,因此个人呼叫交错无关紧要。
答案 2 :(得分:0)
如果您的应用程序已经保证对文件句柄进行序列化访问,那么如果您告诉c-runtime绕过它自己的序列化,则可以获得更好的性能。这是_fread_nolock等功能的目的。