Linux上的C中的stdout线程安全吗?

时间:2009-01-22 03:16:25

标签: c linux unix operating-system

在Linux上使用printf线程安全写入stdout吗?如何使用较低级别的write命令?

5 个答案:

答案 0 :(得分:51)

它没有由C标准指定 - 它取决于您对C标准库的实现。事实上,C标准甚至根本没有提到线程,因为某些系统(例如嵌入式系统)没有多线程。

在GNU实现(glibc)中,stdio中处理FILE*对象的大多数高级函数都是线程安全的。那些名字通常不unlocked的人(例如getc_unlocked(3))。但是,线程安全性处于每个函数调用级别:例如,如果您对printf(3)进行多次调用,则每个调用都保证以原子方式输出,但其他线程可能会在您调用之间打印出来。 printf()。如果您想确保一系列I / O调用以原子方式输出,您可以使用一对flockfile(3)/funlockfile(3)调用来围绕它们以锁定FILE句柄。请注意,这些函数是可重入的,因此您可以安全地在它们之间调用printf(),即使printf()本身调用flockfile(),也不会导致死锁。

write(2)之类的低级I / O调用应该是线程安全的,但我不是百分之百确定 - write()对内核进行系统调用以执行I / O。这究竟是怎么发生取决于你正在使用什么内核。它可能是sysenter指令,或旧系统上的int(中断)指令。进入内核之后,由内核决定I / O是否是线程安全的。在我刚刚使用Darwin内核版本8.11.1进行的测试中,write(2)似乎是线程安全的。

答案 1 :(得分:22)

答案 2 :(得分:13)

它们都是线程安全的,如果多个线程在同一文件描述符上调用它们,应用程序将不会崩溃。但是,如果没有一些应用程序级别的锁定,写入的内容可能是交错的。

答案 3 :(得分:5)

自从提出这个问题(并且最后回答)后,C得到了一个新标准。

C11现在提供多线程支持并解决了流的多线程行为:

  

§7.21.2流

     

¶7当多个执行线程访问流时,每个流都有一个用于 阻止数据竞争 的关联锁 限制多个线程执行的流操作的交错 。一次只有一个线程可以保持此锁定。锁是可重入的:单个线程可以在给定时间多次保持锁。

     

¶8读取,写入,定位或查询流的位置的所有函数在访问流之前锁定流。当访问完成时,它们释放与流关联的锁。

因此,使用C11线程的实现必须保证使用printf是线程安全的。

原子性(如没有交错 1 )是否得到保证,乍一看对我来说并不是那么清楚,因为标准提到了限制交错,而不是阻止,它强制要求数据竞争。

我倾向于保证。标准说明限制交错,因为仍然允许发生一些不改变结果的交错; 例如 fwrite一些字节,fseek支持更多字节fwrite直到原始偏移量,这样两个fwrite s背靠背。该实现可以自由地重新排序这两个fwrite并将它们合并为一个写入。

1:有关示例,请参阅R..'s answer中的删除线文字。

答案 4 :(得分:4)

它是线程安全的; printf应该是可重入的,并且您不会在程序中造成任何陌生或腐败。

您无法保证一个线程的输出不会从另一个线程的输出中途开始。如果您关心这一点,则需要开发自己的锁定输出代码以防止多次访问。