0

我们有一个 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 的整个函数块构建一个锁,所以我看不出提供这种非阻塞函数的意义。

有人可以向我解释这些锁定机制,因为我认为我们的偏执锁定方案会浪费一些性能吗?

先感谢您!

4

3 回答 3

2

对于一些简单的代码,FILE * 内的锁就足够了。考虑一个基本的日志基础设施,您希望所有线程都通过一个通用的 FILE * 进行日志记录。内部锁将确保 FILE * 不会被多个线程破坏,并且由于每个日志行应该独立,因此各个调用交错的方式无关紧要。

于 2009-12-11T20:09:42.430 回答
1

如果您使用 Microsoft 多线程 C 运行时,所有需要全局或静态变量的函数都可以正常工作(例如 printf 和 fread,但不要问我为什么它们需要全局变量)。但是,您仍然不能将 FILE * 结构传递给写入它的函数并期望它是线程安全的。

因此,微软的“线程安全”函数只有在它们是可重入的意义上才是线程安全的,即所有对全局变量和静态变量的访问都是通过互斥锁或类似函数完成的。但并不是说您可以使用相同的 FILE * 同时调用两个 fprintf()。

来源:http: //msdn.microsoft.com/en-us/library/1bh5ewb2%28VS.71%29.aspx

于 2009-12-11T20:03:56.973 回答
0

如果您的应用程序已经保证了对文件句柄的序列化访问,那么如果您告诉 c-runtime 绕过它自己的序列化,您可以获得更好的性能。这就是 _fread_nolock 等函数的用途。

于 2009-12-22T22:21:54.593 回答