MSVCRT的fprintf()实现是否线程安全?

8
似乎glibc的fprintf()实现是线程安全的,但Microsoft的CRT呢?
线程安全不仅仅指避免崩溃,还包括如果多个线程(在同一进程中)调用fprintf(),文本不会混合。
也就是说,例如,如果线程A调用fprintf(stdout,"aaaa");,而线程B调用fprintf(stdout,"bbbb");,则保证不会混合成aabbaabb
是否存在这样的保证?

奇怪:文本似乎仍然混合在一起:https://github.com/git/git/commit/116d1fa6c693e13321dc4c6abe256ca7878e55a5 - VonC
1个回答

14

是的。在多线程运行时库中,每个流都有一个相关联的锁。这个锁会在任何调用printf函数之前获取,并且直到该printf函数返回之前才释放。

这种行为是C11所要求的(在C11之前,标准C中没有“线程”概念)。C11 §7.21.2/7-8规定:

每个流都有一个相关联的锁,用于在多个执行线程访问一个流时防止数据竞争,并限制多个线程执行的流操作的交错。一次只能有一个线程持有此锁。锁是可重入的:单个线程可以同时多次持有锁。

  

所有读取、写入、定位或查询流位置的函数在访问流之前都会锁定流。完成访问后,它们会释放与流相关联的锁。

Visual C++并不完全支持C11,但它确实符合此要求。还有一些关于Visual C++的特定注释:

只要您未定义_CRT_DISABLE_PERFCRIT_LOCKS(仅适用于静态链接的运行时库libcmt.lib和friends),或者使用带_nolock后缀的函数,则对单个流上的大多数操作都是原子性的。

如果您需要在流上执行多个操作时保证原子性,则可以通过使用_lock_file_unlock_file自己获取和释放流锁来获取文件的锁。


这个问题追溯到多久以前?例如,在VC6的多线程CRT中,fprintf()是否是线程安全的? - sashoalm
是的,在VC6多线程运行时库中就是这种情况。 VC6是我现有的最古老的源代码版本;我并不确定这是什么时候实施的。 - James McNellis
@JamesMcNellis 您说:“是的。在多线程运行时库中,每个流都有一个关联的锁。” 我怎么知道我正在使用的库是多线程运行时库? - Trisped
1
@Trisped 自Visual Studio 2005以来,所有运行时库都是多线程的。有关过去版本的详细信息,请参见https://support.microsoft.com/en-us/kb/154753。 - James McNellis
1
有任何支持这个的MSDN链接吗?这个页面并没有提到线程安全和输出交错的问题:https://msdn.microsoft.com/zh-cn/library/xkh07fe2.aspx - Serge Rogatch

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