fread线程锁定的级别是什么?它们需要达到什么级别?

6
Visual Studio的fread函数“锁定其他线程”。 有一个另外的版本_fread_nolock,它可以“在不锁定其他线程的情况下”读取,应该只在“线程安全的上下文中使用,例如单线程应用程序或调用范围已经处理了线程隔离”的情况下使用。
即使在阅读其他相关讨论后,我仍然不清楚locking fread实现的是特定的FILE结构、特定的实际文件还是完全不同文件上的所有fread调用。
如果您使用nolock版本,需要提供什么级别的锁定?多个线程可以并行读取不同文件而不需要任何锁定吗?多个线程可以并行写入不同文件而不需要任何锁定吗?或者是否涉及全局或静态变量会被破坏?
因此,通过使用nolock版本,您能否潜在地实现更好的I/O吞吐量(如果您不需要不必要地移动磁头,例如从不同驱动器或SSD驱动器读取),或者潜在的收益仅仅是将冗余锁定减少到单个锁定(这应该是可以忽略的)。
VS的ifstream.read函数是否与常规fread函数完全相同?(我没有看到它的nolock版本。)

C运行时库的历史可以追溯到操作系统支持线程之前的很久很久以前。规范从未更新过,以说明当两个线程在同一文件上调用fread()时应该发生什么。因此,库编写者必须自己解决旧规范的问题。这不像CRT给程序员提供了另一种方法。试图绕过锁定来实现优化的成功率非常低,I/O速度相当慢。然而,并非所有情况都是如此,例如,locale的代价非常昂贵。试图正确处理的大多数程序的命运是避免使用CRT。 - Hans Passant
2个回答

3
MS标准库实现完全支持多线程。C++标准解释了这个要求:
“27.2.3:如果没有另外指定,多个线程并发访问流对象、流缓冲区对象或 C 库流可能会导致数据竞争。如果一个线程调用的库函数a向流中写入了一个值,并且结果是另一个线程通过库函数b从流中读取该值,而这不会导致数据竞争,则 a 的写入与 b 的读取同步。”
这意味着如果您在流上进行写操作,会进行锁定(不是文件锁定,而是针对使用相同流的所有其他线程进行的并发访问锁定),以确保并发性得到良好管理。
即使不需要,这种锁定开销也始终存在。根据 Microsoft 的说法,这可能会影响性能:
“多线程库的性能得到了改进,与现已淘汰的单线程库的性能接近。对于需要更高性能的情况,有几个新功能。”
这就是为什么提供了_nolock函数。它们直接访问流而不进行线程锁定。必须非常小心地使用,例如:
- 如果您的应用程序是单线程的(使用相同流的另一个进程具有自己的数据结构,并且操作系统在此处管理并发)。 - 如果您确定没有两个线程使用相同的流(例如,如果您只有一个读取器线程并且写入在程序之外完成)。 - 如果您有其他保护关键代码段的同步机制。例如,如果您使用互斥锁或使用原子操作的线程安全非阻塞算法。
在这种情况下,流访问的附加锁定是不需要/冗余的。对于文件密集型函数,可能值得使用no_lock。
注意:正如您指出的那样,仅在进行大量文件访问时才值得使用nolock。

0

在发布之前,我看到了“预期使用是什么”的问题,并感到困惑。原帖作者说“the”(意思是fread)函数会阻止可重入调用,只允许一个线程整体进入该函数。但是,另一个答案表明锁定实际上是在FILE级别上进行的。另一个答案说线程安全版本是可重入的,但您不能使用相同的FILE调用两次。另一个答案说使用_nolock版本可以获得更好的性能,但没有提到实际磁盘I/O是否更高,或者它是否只是绕过冗余锁定。这让我有更多的问题。 - user1902689

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接