当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《Linux上的C中的stdout线程安全吗?》经验,为你挑选了5个好方法。

printf在Linux上使用线程安全写入stdout ?使用低级write命令怎么样?



1> Adam Rosenfi..:

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

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

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


这个答案忽略了这个问题被标记为unix/linux.POSIX要求stdio是线程安全的,这是非常不幸的,因为它会杀死性能,因为没有实际的方法可以从多个线程对相同的FILE进行操作(数据将无可救药地交错出现;原子性仅在字符级别).
多线程在嵌入式系统中很常见.
可能很高兴为C11更新这个答案.

2> R....:

你是否称它为"线程安全"取决于你对线程安全的定义.POSIX要求stdio函数使用锁定,因此FILE如果printf从多个线程同时使用,程序不会崩溃,损坏对象状态等.然而,所有的stdio操作在重复调用的术语正式指定fgetcfputc,所以保证没有较大规模的原子.也就是说,如果线程1和2尝试打印"Hello\n",并"Goodbye\n"在同一时间,但也不能保证输出将是要么"Hello\nGoodbye\n"还是"Goodbye\nHello\n".它也可以"HGelolodboy\ne\n".在实践中,大多数实现将为整个更高级别的写入调用获取单个锁定,因为它更有效,但您的程序不应该这样做.可能存在未完成此事的极端情况; 例如,实现可能完全省略对非缓冲流的锁定.

编辑:上面关于原子性的文字不正确.POSIX保证所有stdio操作都是原子操作,但保证隐藏在以下文档中flockfile:http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html

引用(FILE*)对象的所有函数都应该在内部使用flockfile()和funlockfile()来获取这些(FILE*)对象的所有权.

您可以使用flockfile,ftrylockfilefunlockfile功能自己实现更大高于单功能调用原子写入.



3> Dave Ray..:

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



4> a3f..:

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

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

§7.21.2流

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

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

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

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

我倾向于保证.该标准提到限制交错,因为仍然允许发生一些不改变结果的交错; 例如, fwrite一些字节,fseek返回更多,fwrite直到原始偏移,以便两个fwrites背对背.该实现可以自由地重新排序这些2 fwrite并将它们合并为一个写入.


1:请参阅R ..的答案中的删除线文字.



5> Adam Hawes..:

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

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


我不知道其他人会做什么,但是GNU C库默认情况下是线程安全的,因此不会使用相同的缓冲区。
推荐阅读
殉情放开那只小兔子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有