简体   繁体   English

std::cout 是线程安全的,但会导致竞争条件吗?

[英]std::cout is thread safe, but can cause race conditions?

I'm looking at an online course for C++ multithreading, and I see the following:我正在查看 C++ 多线程的在线课程,我看到以下内容:

在此处输入图像描述

If std::cout can have race conditions, then how is it that it's thread-safe?如果std::cout可以有竞争条件,那么它是线程安全的吗? Isn't thread-safety, by definition, free of race conditions?根据定义,线程安全不是没有竞争条件吗?

It's distinguishing between the ordering of calls to operator<< and outputting of each character as a result of those calls.它区分了对operator<<的调用顺序和作为这些调用结果的每个字符的输出。

Specifically, there is no conflict between threads on a per-character basis but, if you have two threads output pax and diablo respectively, you may end up with any of (amongst others):具体来说,每个字符的线程之间没有冲突,但是,如果你有两个线程 output paxdiablo ,你可能会得到任何一个(以及其他):

paxdiablo
diablopax
pdaixablo
padiablox
diapaxblo

What you quoted text is stating is that there will be no intermixing of the output of (for example) p and d that would cause a data race.您引用的文字说明的是,(例如) pd的 output 不会混合,这会导致数据竞争。

And a race condition isn't necessarily a problem, nor a thread-safety issue, it just means the behaviour can vary based on ordering.竞争条件不一定是问题,也不一定是线程安全问题,它只是意味着行为可能会因顺序而异。 It can become such an issue if the arbitrary ordering can corrupt data in some way but that's not the case here (unless you consider badly formatted output to be corrupt).如果任意排序可能会以某种方式损坏数据,则可能会成为这样的问题,但此处并非如此(除非您认为格式错误的 output 已损坏)。


It's similar to the statement:它类似于声明:

result = a * 7 + b * 4;

ISO C++ doesn't actually mandate the order in which a * 7 or b * 4 is evaluated so you could consider that a race condition (it's quite plausible, though unlikely, that the individual calculations could be handed off to separate CPUs for parallelism), but in no way is that going to be a problem. ISO C++ 实际上并没有规定评估a * 7b * 4的顺序,因此您可以考虑这是一种竞争条件(尽管不太可能,单个计算可以移交给单独的 CPU 以实现并行性,这是很合理的) ,但这绝不是一个问题。


Interestingly, ISO C++ makes no mention of race conditions (which may become a problem), only of data races (which are almost certainly a problem).有趣的是,ISO C++ 没有提到竞争条件(这可能会成为一个问题),只提到数据竞争(这几乎肯定是一个问题)。

There are many levels of thread safety.线程安全有很多级别。 Some programs must complete in exactly the same order every time.有些程序每次都必须以完全相同的顺序完成。 They must be as-if they were executed in a single thread.它们必须就像在单个线程中执行一样。 Others permit some level of flexibility in the order in which statements are executed.其他允许在执行语句的顺序上具有一定程度的灵活性。 The spec's statements about std::cout state that it is in the latter category.规范关于std::cout state 的声明属于后者。 The order that characters will be printed to the screen is unspecified.未指定字符打印到屏幕的顺序。

You can see the two behaviors in the act of compiling.您可以在编译过程中看到这两种行为。 In most cases, we don't care about the order that things get compiled in. We happily type make -j8 and have 8 threads (processes) compiling on parallel.在大多数情况下,我们不关心编译的顺序。我们愉快地键入make -j8并有 8 个线程(进程)并行编译。 Whatever thread does the work is fine by us.无论线程做什么工作对我们来说都很好。 On the other hand, consider the compiling requirements of NixOS .另一方面,考虑NixOS的编译要求。 In that distro, you compile every application yourself and cross-check it against hashes posted by the authors.在该发行版中,您自己编译每个应用程序,并与作者发布的哈希值进行交叉检查。 When compiling on NixOS, within a compilation unit (one.c or.cpp), you absolutely must operate as-if there is a single thread because you need the exact same product deterministically -- every time.在 NixOS 上编译时,在一个编译单元(一个.c 或.cpp)内,您绝对必须像有一个线程一样操作,因为您每次都需要完全相同的产品。

There is a darker version of this kind of race behavior known as a "data race."这种竞争行为有一个更黑暗的版本,称为“数据竞争”。 This is when two writers try to write the same variable at the same time, or a reader tries to read it while a writer tries to write to it, all without any synchronization.这是当两个写入器尝试同时写入同一个变量,或者读取器尝试读取它而写入器尝试写入它时,所有这些都没有任何同步。 This is undefined behavior.这是未定义的行为。 Indeed, one of my favorite pages on programming goes painstakingly through all the things that can go wrong if there is a data rate.的确, 我最喜欢的编程页面之一煞费苦心地介绍了如果有数据速率可能会出错的所有事情。

So what is the spec saying about needing to lock cout?那么规范中关于需要锁定 cout 的内容是什么? What it's saying is that the interthread behavior is specified, and that there is no data race.它的意思是指定了线程间行为,并且没有数据竞争。 Thus, you do not need to guard the output stream.因此,您不需要保护 output stream。

Typically, if you find you want to guard it, it's a good time to take a step back and evaluate your choices.通常,如果您发现自己想要保护它,那么现在是退后一步并评估您的选择的好时机。 Sure, you can put a mutex on your usage of cout , but you can't put it on any libraries that you are using, so you'll still have issues with interleaving if they print anything.当然,您可以在使用cout时设置互斥锁,但不能将其放在正在使用的任何库中,因此如果它们打印任何内容,您仍然会遇到交错问题。 It may be a good time to look at your output system.现在可能是查看您的 output 系统的好时机。 You may want to start passing messages to a centralized pool (protected by a mutex), and have one thread that writes them out to the screen.您可能希望开始将消息传递到集中式池(受互斥体保护),并有一个线程将它们写到屏幕上。

The text is there to warn you that building some thing entirely out of "thread-safe" components does not guarantee that the thing you built will be "thread-safe."那里的文字警告您,完全用“线程安全”组件构建某些东西并不能保证您构建的东西将是“线程安全的”。

The std::cout stream is called "thread-safe" because using it in multiple threads without any other synchronization will not break any of its underlying mechanism. std::cout stream 被称为“线程安全”,因为在没有任何其他同步的情况下在多个线程中使用它不会破坏其任何底层机制。 The cout stream will behave in predictable, reasonable ways; cout stream 将以可预测、合理的方式运行; Everything that various threads write will appear in the output, nothing will appear that wasn't written by some thread, everything written by any one thread will appear in the same order that the one thread wrote it, etc. Also, it won't throw unexpected exceptions, or segfault your program, or overwrite random variables.各种线程写的所有东西都会出现在output中,不会出现不是某个线程写的东西,任何一个线程写的所有东西都会按照一个线程写的顺序出现,等等。而且,它不会抛出意外的异常,或程序段错误,或覆盖随机变量。

But, if your program allows several threads to "race" to write std::cout , there is nothing that the authors of the library could do to help predict which thread will win.但是,如果您的程序允许多个线程“竞相”编写std::cout ,则库的作者无法帮助预测哪个线程将获胜。 If you expect to see data written by several threads appear in some particular order, then it's your responsibility to ensure that the threads call the library in that same order.如果您希望看到由多个线程写入的数据以某种特定顺序出现,那么有责任确保线程以相同的顺序调用库。

Don't forget that a statement like this is not one std::cout function call;不要忘记这样的语句不是一个std::cout function 调用; it's three separate calls.这是三个单独的电话。

std::cout << "there are " << number_of_doodles << " doodles here.\n";

If some other thread concurrently executes this statement (also three calls):如果某个其他线程同时执行此语句(也是三个调用):

std::cout << "I like " << name_of_favorite_pet << ".\n";

Then the output of the six separate std::cout calls could be interleaved, eg:然后可以交错六个单独的std::cout调用的 output ,例如:

I like there are 5 fido doodles here.
.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM