cout是同步/线程安全的吗?
一般来说,我认为stream不同步,取决于用户做适当的locking。 但是,像cout
这样的事情在标准库中得到特殊待遇吗?
也就是说,如果多个线程正在写入cout
可以破坏cout
对象? 我明白,即使同步你仍然得到随机交错的输出,但交叉保证。 也就是说,从多个线程使用cout
是安全的吗?
这个供应商是否依赖? gcc做什么?
重要提示 :如果您说“是”,请为您的答案提供一些参考,因为我需要某种certificate。
我关心的不是底层的系统调用,这些都很好,但是这些stream在顶部添加了一层缓冲。
C ++ 03标准对此没有任何说明。 当你对线程安全性没有保证的时候,你应该把它看作是不是线程安全的。
这里特别感兴趣的是cout
被缓冲的事实。 即使write
的调用(或者在那个特定的实现中实现了这种效果的任何东西)被保证是互斥的,缓冲区可能被不同的线程共享。 这将很快导致腐败的内部状态的stream。
即使访问缓冲区是保证线程安全的,你认为在这个代码中会发生什么?
// in one thread cout << "The operation took " << result << " seconds."; // in another thread cout << "Hello world! Hello " << name << "!";
你可能希望每一行在这里互相排斥。 但是,一个实现如何保证呢?
在C ++ 11中,我们确实有一些保证。 FDIS在§27.4.1[iostream.objects.overview]中说明如下:
对同步(第27.5.3.4节)标准iostream对象的格式化和非格式化input(第27.7.2.1节)和输出(第27.7.3.1节)函数或multithreading的标准Cstream的并发访问不应导致数据竞争1.10)。 [注意:如果用户希望避免交错字符,用户必须同时使用多个线程同时使用这些对象和stream。 – 结束注意]
所以,你不会得到损坏的stream,但是如果你不希望输出是垃圾的,你仍然需要手动同步它们。
这是一个很好的问题。
首先,C ++ 98 / C ++ 03没有“线程”的概念。 所以在这个世界上,这个问题是没有意义的。
那么C ++ 0x呢? 见Martinho的答案 (我承认我很惊讶)。
特定实现前的C ++ 0x如何? 那么,举个例子,下面是GCC 4.5.2(“streambuf”头文件)中的basic_streambuf<...>:sputc
的源代码:
int_type sputc(char_type __c) { int_type __ret; if (__builtin_expect(this->pptr() < this->epptr(), true)) { *this->pptr() = __c; this->pbump(1); __ret = traits_type::to_int_type(__c); } else __ret = this->overflow(traits_type::to_int_type(__c)); return __ret; }
显然,这不会执行locking。 xsputn
也一样。 这肯定是cout使用的streambuftypes。
据我所知,libstdc ++不会对任何stream操作执行任何locking。 我不会期望任何事情,因为这会很慢。
所以在这个实现中,显然有可能两个线程的输出互相破坏( 而不是交错)。
这个代码是否会破坏数据结构本身? 答案取决于这些function可能的相互作用。 例如,如果一个线程试图刷新缓冲区而另一个线程尝试调用xsputn
或其他东西,会发生什么情况。 这可能取决于你的编译器和CPU如何决定重新sorting内存加载和存储; 这将需要仔细分析。 这也取决于你的CPU如果两个线程试图同时修改相同的位置。
换句话说,即使它在当前环境中正常工作,在更新任何运行时,编译器或CPU时,也可能会中断。
执行摘要:“我不会”。 构build一个适当locking的日志logging类,或者移到C ++ 0x。
作为一个弱select,你可以将cout设置为无缓冲。 可能(尽pipe不能保证)会跳过与缓冲区相关的所有逻辑并直接调用write
。 虽然这可能是非常缓慢。
C ++标准没有指定写入stream是否是线程安全的,但通常不是。
http://www.techrepublic.com/article/use-stl-streams-for-easy-c-plus-plus-thread-safe-logging
还有: C ++中的标准输出stream是否是线程安全的(cout,cerr,clog)?
UPDATE
请看@Martinho Fernandes的答案,以了解新标准C ++ 11讲述的内容。
正如其他答案所提到的,这绝对是供应商特定的,因为C ++标准没有提到线程(C ++ 0x中的这一变化)。
GCC并没有对线程安全和I / O做出很多的承诺。 但是它承诺的文档在这里:
关键的东西可能是:
__basic_filetypes仅仅是C stdio层周围的小包装的集合(同样,请参阅Structure下的链接)。 我们不会locking自己,而只是通过打电话给fopen,fwrite等等。
因此,对于3.0,“multithreading安全的I / O”的问题必须回答“是您的平台的C库线程安全的I / O? 有些是默认的,有些则不是。 许多提供了C库的多个实现,具有不同的线程安全性和效率折衷。 你,程序员,总是需要照顾多个线程。
(例如,POSIX标准要求C stdio FILE *操作是primefaces的,符合POSIX标准的C库(例如,在Solaris和GNU / Linux上)有一个内部互斥体来对FILE *上的序列化操作,但是你仍然需要不要做愚蠢的事情,比如在一个线程中调用fclose(fs),然后在另一个线程中访问fs。)
因此,如果您的平台的C库是线程安全的,那么您的fstream I / O操作将在最低级别进行线程安全。 对于更高级别的操作,例如操纵stream格式化类中包含的数据(例如,在std :: ofstream中设置callback),您需要像其他任何关键共享资源一样保护这些访问。
我不知道是否有任何改变正弦3.0提到的时间表。
MSVC的iostreams
的线程安全文档可以在这里find: http : //msdn.microsoft.com/en-us/library/c9ceah3b.aspx :
单个对象对于从多个线程读取是线程安全的。 例如,给定一个对象A,同时从线程1和线程2读取A是安全的。
如果单个对象正在被一个线程写入,则必须保护所有对同一线程或其他线程的读写操作。 例如,给定一个对象A,如果线程1正在写入A,则线程2必须被阻止读取或写入A.
即使另一个线程正在读取或写入相同types的不同实例,读取和写入types的一个实例也是安全的。 例如,给定对象A和B是相同types的,如果在线程1中写入A,而在线程2中读取B,则是安全的。
…
iostream类
iostream类遵循与其他类相同的规则,只有一个例外。 从多个线程写入对象是安全的。 例如,线程1可以与线程2同时写入cout。但是,这可能会导致两个线程的输出混合在一起。
注意:从stream缓冲区中读取不被认为是读取操作。 它应该被认为是一个写操作,因为这改变了这个类的状态。
请注意,该信息适用于最新版本的MSVC(当前用于VS 2010 / MSVC 10 / cl.exe
16.x)。 您可以使用页面上的下拉控件select较旧版本的MSVC的信息(而旧版本的信息也不同)。