stdout线程安全在Linux上的C?

在Linux上使用printf线程安全写入标准输出? 那么使用底层write命令呢?

Solutions Collecting From Web of "stdout线程安全在Linux上的C?"

它不是由C标准规定的 – 它取决于你的C标准库的实现。 事实上,C标准甚至没有提到线程,因为某些系统(如嵌入式系统)没有多线程。

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

write(2)等低级别的I / O调用应该是线程安全的,但是我不是100%肯定的 – write()将系统调用到内核中执行I / O。 这个怎么发生取决于你正在使用的内核。 它可能是sysenter指令,或者旧系统上的int (中断)指令。 一旦进入内核,就要由内核来确保I / O是线程安全的。 在一个测试中,我只是用Darwin coreel Version 8.11.1做的, write(2)似乎是线程安全的。

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

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

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

您可以自己使用flockfileftrylockfilefunlockfile函数来实现大于单一函数调用的原子写入。

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

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

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

自从这个问题被问及(最后回答)以来,C得到了一个新的标准。

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

§7.21.2流

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

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

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

是否保证了原子性(如无交织1 )是一目了然的,因为这个标准提到了限制交织,而不是防止 ,它是数据竞赛所要求的。

我倾向于保证。 该标准提到了限制交织,因为一些不改变结果的交织仍然被允许发生; 例如, fwrite一些字节,然后再fseek一些字节,直到原始的偏移量,这样两个fwrite就是背靠背的。 这个实现可以自由地对这两个fwrite进行重新排序,并将它们合并成一个单独的写入。


1 :以R ..的答案为例。