Linux上C中的标准线程安全?

Linux上C中的标准线程安全?,c,linux,unix,operating-system,C,Linux,Unix,Operating System,在Linux上使用printfthread安全吗?使用较低级别的write命令怎么样?它是线程安全的;printf应该是可重入的,并且不会在程序中造成任何奇怪或损坏 您不能保证一个线程的输出不会从另一个线程的输出的一半开始。如果您关心这一点,您需要开发自己的锁定输出代码以防止多重访问。它们都是线程安全的,如果多个线程在同一文件描述符上调用它们,您的应用程序不会崩溃。但是,如果没有一些应用程序级锁定,所写的任何内容都可以交错进行。它不是由C标准指定的——这取决于您对C标准库的实现。事实上,C标准甚

在Linux上使用
printf
thread安全吗?使用较低级别的
write
命令怎么样?

它是线程安全的;printf应该是可重入的,并且不会在程序中造成任何奇怪或损坏


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

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

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

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

低级I/O调用,如
write(2)
,应该是线程安全的,但我不能100%确定-
write()
对内核进行系统调用以执行I/O。具体发生的方式取决于您使用的内核。它可能是
sysenter
指令,也可能是较旧系统上的
int
(中断)指令。一旦进入内核,就由内核来确保I/O是线程安全的。在我刚刚对Darwin内核版本8.11.1所做的测试中,
write(2)
似乎是线程安全的。

是否称之为“线程安全”取决于您对线程安全的定义。POSIX需要
stdio
函数来使用锁定,因此如果从多个线程同时使用
printf
,程序将不会崩溃、损坏
文件
对象状态等。但是,所有的
stdio
操作都是根据对
fgetc
fputc
的重复调用来正式指定的,因此不保证更大规模的原子性。也就是说,如果线程1和线程2尝试同时打印
“Hello\n”
“再见\n”
,则不能保证输出是
“Hello\ngoodbeye\n”
“再见\nHello\n”
。它也可以是
“HGelolodboy\ne\n”
。在实践中,大多数实现都会为整个更高级别的写调用获取一个锁,这仅仅是因为它更高效,但您的程序不应该这样认为。在某些情况下,可能没有这样做;例如,一个实现可能完全忽略对未缓冲流的锁定

编辑:上述关于原子性的文本不正确。POSIX保证所有的
stdio
操作都是原子操作,但该保证隐藏在
flockfile
的文档中:

引用(FILE*)对象的所有函数的行为应与它们在内部使用flockfile()和funlockfile()来获得这些(FILE*)对象的所有权一样


您可以自己使用
flockfile
ftrylockfile
funlockfile
函数来实现比单个函数调用原子写入更大的功能。

C自从提出这个问题(并在上次得到回答)以来,获得了一个新的标准

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

§7.21.2流

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

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

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

我一眼就不清楚原子性(如无交错)是否得到保证,因为该标准提到限制交错,而不是防止交错,这是数据竞争的强制要求

我倾向于得到保证。该标准提到限制交织,因为一些不会改变结果的交织仍然被允许发生;e、 g.
fwrite
一些字节,
fseek
返回一些字节,并
fwrite
直到原始偏移量,以便两个
fwrite
都是背靠背的。该实现可以自由地对这2个
fwrite
s进行重新排序,并将它们合并到单个写入中



:有关示例,请参阅中的删除文本。

对printf的所有调用都可能使用相同的缓冲区来生成字符串。许多实现还在scanf和printf之间共享一个缓冲区