C++ cout 是同步的/线程安全的吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6374264/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is cout synchronized/thread-safe?
提问by edA-qa mort-ora-y
In general I assume that streams are not synchronized, it is up to the user to do appropriate locking. However, do things like cout
get special treatment in the standard library?
一般来说,我假设流是不同步的,由用户来做适当的锁定。但是,cout
在标准库中,诸如此类的东西会得到特殊处理吗?
That is, if multiple threads are writing to cout
can they corrupt the cout
object? I understand that even if synchronized you'd still get randomly interleaved output, but is that interleaving guaranteed. That is, is it safe to use cout
from multiple threads?
也就是说,如果多个线程正在写入,cout
它们会损坏cout
对象吗?我知道即使同步你仍然会得到随机交错的输出,但是交错是有保证的。也就是说,cout
从多个线程使用是否安全?
Is this vendor dependent? What does gcc do?
该供应商是否依赖?gcc 是做什么的?
Important: Please provide some kind of reference for your answer if you say "yes" since I need some kind of proof of this.
重要提示:如果您说“是”,请为您的回答提供某种参考,因为我需要某种证明。
My concern is also not about the underlying system calls, those are fine, but the streams add a layer of buffering on top.
我关心的也不是底层系统调用,这些都很好,但是流在上面添加了一层缓冲。
采纳答案by R. Martinho Fernandes
The C++03 standard does not say anything about it. When you have no guarantees about the thread-safety of something, you should treat it as not thread-safe.
C++03 标准对此没有任何说明。当您无法保证某事物的线程安全性时,您应该将其视为非线程安全的。
Of particular interest here is the fact that cout
is buffered. Even if the calls to write
(or whatever it is that accomplishes that effect in that particular implementation) are guaranteed to be mutually exclusive, the buffer might be shared by the different threads. This will quickly lead to corruption of the internal state of the stream.
这里特别有趣的cout
是缓冲的事实。即使对write
(或在该特定实现中实现该效果的任何东西)的调用保证是互斥的,缓冲区也可能由不同的线程共享。这将很快导致流内部状态的损坏。
And even if access to the buffer is guaranteed to be thread-safe, what do you think will happen in this code?
即使保证对缓冲区的访问是线程安全的,您认为这段代码会发生什么?
// in one thread
cout << "The operation took " << result << " seconds.";
// in another thread
cout << "Hello world! Hello " << name << "!";
You probably want each line here to act in mutual exclusion. But how can an implementation guarantee that?
您可能希望此处的每一行都相互排斥。但是一个实现如何保证呢?
In C++11, we do have some guarantees. The FDIS says the following in §27.4.1 [iostream.objects.overview]:
在 C++11 中,我们确实有一些保证。FDIS 在 §27.4.1 [iostream.objects.overview] 中说明以下内容:
Concurrent access to a synchronized (§27.5.3.4) standard iostream object's formatted and unformatted input (§27.7.2.1) and output (§27.7.3.1) functions or a standard C stream by multiple threads shall not result in a data race (§1.10). [ Note: Users must still synchronize concurrent use of these objects and streams by multiple threads if they wish to avoid interleaved characters. — end note ]
多线程对同步(第 27.5.3.4 节)标准 iostream 对象的格式化和未格式化输入(第 27.7.2.1 节)和输出(第 27.7.3.1 节)函数或标准 C 流的并发访问不应导致数据竞争(第1.10)。[注意:如果用户希望避免交错字符,他们仍然必须同步多个线程对这些对象和流的并发使用。— 尾注 ]
So, you won't get corrupted streams, but you still need to synchronize them manually if you don't want the output to be garbage.
因此,您不会得到损坏的流,但如果您不希望输出成为垃圾,您仍然需要手动同步它们。
回答by Nemo
This is a great question.
这是一个很好的问题。
First, C++98/C++03 has no concept of "thread". So in that world, the question is meaningless.
首先,C++98/C++03没有“线程”的概念。所以在那个世界里,这个问题毫无意义。
What about C++0x? See Martinho's answer(which I admit surprised me).
C++0x 呢?请参阅Martinho 的回答(我承认这让我感到惊讶)。
How about specific implementations pre-C++0x? Well, for example, here is the source code for basic_streambuf<...>:sputc
from GCC 4.5.2 ("streambuf" header):
C++0x 之前的具体实现怎么样?好吧,例如,这里是basic_streambuf<...>:sputc
来自 GCC 4.5.2的源代码(“streambuf”标头):
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;
}
Clearly, this performs no locking. And neither does xsputn
. And this is definitely the type of streambuf that cout uses.
显然,这不执行锁定。也没有xsputn
。这绝对是 cout 使用的流缓冲类型。
As far as I can tell, libstdc++ performs no locking around any of the stream operations. And I would not expect any, as that would be slow.
据我所知,libstdc++ 不会对任何流操作执行锁定。我不期望任何,因为那会很慢。
So with this implementation, obviously it is possible for two threads' output to corrupt each other (notjust interleave).
所以这个实现,显然是可能的两个线程输出腐败对方(不只是交错)。
Could this code corrupt the data structure itself? The answer depends on the possible interactions of these functions; e.g., what happens if one thread tries to flush the buffer while another tries to call xsputn
or whatever. It might depend on how your compiler and CPU decide to reorder memory loads and stores; it would take a careful analysis to be sure. It also depends what your CPU does if two threads try to modify the same location concurrently.
这段代码会破坏数据结构本身吗?答案取决于这些功能之间可能的相互作用;例如,如果一个线程尝试刷新缓冲区而另一个线程尝试调用xsputn
或其他什么,会发生什么。这可能取决于您的编译器和 CPU 如何决定重新排序内存加载和存储;需要仔细分析才能确定。如果两个线程尝试同时修改同一位置,这还取决于您的 CPU 会做什么。
In other words, even if it happens to work fine in your current environment, it might break when you update any of your runtime, compiler, or CPU.
换句话说,即使它碰巧在您当前的环境中运行良好,当您更新任何运行时、编译器或 CPU 时,它也可能会中断。
Executive summary: "I wouldn't". Build a logging class that does proper locking, or move to C++0x.
执行摘要:“我不会”。构建一个执行正确锁定的日志记录类,或移至 C++0x。
As a weak alternative, you could set cout to unbuffered. It is likely (although not guaranteed) that would skip all logic related to the buffer and call write
directly. Although that might be prohibitively slow.
作为一个弱替代方案,您可以将 cout 设置为无缓冲。很可能(虽然不能保证)跳过与缓冲区相关的所有逻辑并write
直接调用。尽管这可能会非常缓慢。
回答by phoxis
The C++ Standard does not specify whether writing to streams is thread-safe, but usually it's not.
C++ 标准没有指定写入流是否是线程安全的,但通常不是。
www.techrepublic.com/article/use-stl-streams-for-easy-c-plus-plus-thread-safe-logging
www.techrepublic.com/article/use-stl-streams-for-easy-c-plus-plus-thread-safe-logging
and also: Are standard output streams in C++ thread-safe (cout, cerr, clog)?
还有:C++ 中的标准输出流是线程安全的(cout、cerr、clog)吗?
UPDATE
更新
Please have a look at @Martinho Fernandes' answer to know about what the new standard C++11 tells about this.
请查看@Martinho Fernandes 的回答,了解新标准 C++11 对此有何看法。
回答by Michael Burr
As other answers mention, this is definitely vendor-specific since the C++ standard makes no mention of threading (this changes in C++0x).
正如其他答案所提到的,这绝对是特定于供应商的,因为 C++ 标准没有提到线程(这在 C++0x 中发生了变化)。
GCC doesn't make a whole lot of promises about thread safety and I/O. But the documentation for what it does promise is here:
GCC 并没有对线程安全和 I/O 做出很多承诺。但它所承诺的文件在这里:
the key stuff is probably:
关键的东西可能是:
The __basic_file type is simply a collection of small wrappers around the C stdio layer (again, see the link under Structure). We do no locking ourselves, but simply pass through to calls to fopen, fwrite, and so forth.
So, for 3.0, the question of "is multithreading safe for I/O" must be answered with, "is your platform's C library threadsafe for I/O?" Some are by default, some are not; many offer multiple implementations of the C library with varying tradeoffs of threadsafety and efficiency. You, the programmer, are always required to take care with multiple threads.
(As an example, the POSIX standard requires that C stdio FILE* operations are atomic. POSIX-conforming C libraries (e.g, on Solaris and GNU/Linux) have an internal mutex to serialize operations on FILE*s. However, you still need to not do stupid things like calling fclose(fs) in one thread followed by an access of fs in another.)
So, if your platform's C library is threadsafe, then your fstream I/O operations will be threadsafe at the lowest level. For higher-level operations, such as manipulating the data contained in the stream formatting classes (e.g., setting up callbacks inside an std::ofstream), you need to guard such accesses like any other critical shared resource.
__basic_file 类型只是 C stdio 层周围的小包装器的集合(再次参见 Structure 下的链接)。我们不锁定自己,而是简单地传递对 fopen、fwrite 等的调用。
因此,对于 3.0,“多线程对于 I/O 是否安全”的问题必须回答为“您平台的 C 库对于 I/O 是否线程安全?” 有些是默认的,有些不是;许多提供了 C 库的多种实现,并在线程安全性和效率之间进行了不同的权衡。您,程序员,总是需要注意多线程。
(例如,POSIX 标准要求 C stdio FILE* 操作是原子的。符合 POSIX 的 C 库(例如,在 Solaris 和 GNU/Linux 上)有一个内部互斥锁来序列化对 FILE* 的操作。但是,您仍然需要不要做愚蠢的事情,例如在一个线程中调用 fclose(fs) 然后在另一个线程中访问 fs。)
因此,如果您平台的 C 库是线程安全的,那么您的 fstream I/O 操作将在最低级别是线程安全的。对于更高级别的操作,例如操作流格式化类中包含的数据(例如,在 std::ofstream 内设置回调),您需要像任何其他关键共享资源一样保护此类访问。
I don't know if anything has changed sine the 3.0 timeframe mentioned.
我不知道在提到的 3.0 时间范围内是否有任何变化。
MSVC's thread safety documentation for iostreams
can be found here: http://msdn.microsoft.com/en-us/library/c9ceah3b.aspx:
iostreams
可以在此处找到MSVC 的线程安全文档:http: //msdn.microsoft.com/en-us/library/c9ceah3b.aspx:
A single object is thread safe for reading from multiple threads. For example, given an object A, it is safe to read A from thread 1 and from thread 2 simultaneously.
If a single object is being written to by one thread, then all reads and writes to that object on the same or other threads must be protected. For example, given an object A, if thread 1 is writing to A, then thread 2 must be prevented from reading from or writing to A.
It is safe to read and write to one instance of a type even if another thread is reading or writing to a different instance of the same type. For example, given objects A and B of the same type, it is safe if A is being written in thread 1 and B is being read in thread 2.
...
iostream Classes
The iostream classes follow the same rules as the other classes, with one exception. It is safe to write to an object from multiple threads. For example, thread 1 can write to cout at the same time as thread 2. However, this can result in the output from the two threads being intermixed.
Note: Reading from a stream buffer is not considered to be a read operation. It should be considered as a write operation, because this changes the state of the class.
单个对象对于从多个线程读取是线程安全的。例如,给定一个对象 A,同时从线程 1 和线程 2 读取 A 是安全的。
如果一个线程正在写入单个对象,则必须保护在同一线程或其他线程上对该对象的所有读取和写入。例如,给定一个对象 A,如果线程 1 正在写入 A,则必须阻止线程 2 读取或写入 A。
即使另一个线程正在读取或写入同一类型的不同实例,读取和写入一个类型的一个实例也是安全的。例如,给定相同类型的对象 A 和 B,如果 A 在线程 1 中写入而 B 在线程 2 中读取是安全的。
...
iostream 类
iostream 类遵循与其他类相同的规则,只有一个例外。从多个线程写入对象是安全的。例如,线程 1 可以与线程 2 同时写入 cout。但是,这可能导致两个线程的输出混合。
注意:从流缓冲区读取不被视为读取操作。它应该被视为写操作,因为这会改变类的状态。
Note that that information is for the most recent version of MSVC (currently for VS 2010/MSVC 10/cl.exe
16.x). You can select the information for older versions of MSVC using a dropdown control on the page (and the information is different for older versions).
请注意,该信息适用于最新版本的 MSVC(当前适用于 VS 2010/MSVC 10/ cl.exe
16.x)。您可以使用页面上的下拉控件选择旧版本 MSVC 的信息(旧版本的信息不同)。