How to easily make std::cout thread-safe?
Note: This answer is pre-C++20 so it does not use std::osyncstream
with its separate buffering, but uses a lock instead.
I guess you could implement your own class which wraps cout
and associates a mutex with it. The operator <<
of that new class would do three things:
- create a lock for the mutex, possibly blocking other threads
- do the output, i.e. do the operator
<<
for the wrapped stream and the passed argument - construct an instance of a different class, passing the lock to that
This different class would keep the lock and delegate operator <<
to the wrapped stream. The destructor of that second class would eventually destroy the lock and release the mutex.
So any output you write as a single statement, i.e. as a single sequence of <<
invocations, will be printed atomically as long as all your output goes through that object with the same mutex.
Let's call the two classes synchronized_ostream
and locked_ostream
. If sync_cout
is an instance of synchronized_ostream
which wraps std::cout
, then the sequence
sync_cout << "Hello, " << name << "!" << std::endl;
would result in the following actions:
synchronized_ostream::operator<<
would aquire the locksynchronized_ostream::operator<<
would delegate the printing of "Hello, " tocout
operator<<(std::ostream&, const char*)
would print "Hello, "synchronized_ostream::operator<<
would construct alocked_ostream
and pass the lock to thatlocked_ostream::operator<<
would delegate the printing ofname
tocout
operator<<(std::ostream&, std::string)
would print the name- The same delegation to
cout
happens for the exclamation point and the endline manipulator - The
locked_ostream
temporary gets destructed, the lock is released
Is cout synchronized/thread-safe?
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.
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.
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]:
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 ]
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.
Thread safe std::cout
Your problem is here: std::lock_guard<std::mutex>{global_mtx};
creates a lock guard and immediately releases it. You need to create a variable to hold the lock, like std::lock_guard<std::mutex> lock{global_mtx};
.
Is writing a single string to cout thread safe in C++98
As has been pointed out in the comments, C++98 doesn't offer any notion of threads in the standard, so you can't answer this question by reference to the standard.
However any reasonable C++98 implementation intended to be used with threads (i.e., most of them outside of some embedded markets) would almost certainly offer at least a more-or-less safe std::cout
for "plain" use cases. After all, using that stream for output is very common, and using it across threads in a threaded program will also be very common.
Unfortunately, that's about as much as you can say with specificity. Note, in particular, that I'm not even defining "safe" in a particular way. At least you probably expect that it won't crash, corrupt your program or produce output "out of thin air". Beyond that, it depends.
What Does it Depend On
The next step is to check the implementation itself. Hopefully you have the source1!
Typically you'll find that the implementation has some thread-agnostic code (e.g,. copying into stack-local buffers) and at some point locks and then manipulates shared state inside the std::cout
object. When and where it locks is important.
Single String
For example, one would hope that the output of a single item on two threads, like std::cout << "AAAA"
on thread 1 and std::cout << "BBBB"
on thread 2 would at least result in "not-interleaved" output of AAAABBBB
or BBBBAAAA
but never BBAAAABB
or something like that. This may not be true for implementations that buffer in certain ways! For example, the implementation may lock, then copy as much into an internal buffer as possible and if full, output, and then unlock and proceed again.
Keep in mind that even the std::cout
object is totally locked (i.e., essentially the entire operator<<(const char *)
is run under a lock), you might see interleaving of even single string output when it is run concurrently with other code that is writing to stdout
but through a mechanism other than std::cout
- such as printf()
.
In this case the C/C++ runtimes generally won't share any lock, and you'll be relying on the locking of the underlying write()
-type call offered by the OS. Although this call is fully locked and thread-safe, it might not write the entire string in one shot (this is common, for example, when an intermediate buffer fills up such as when writing to a pipe).
Multiple Operators
The main problem with the stream interface is multiple operators. Something like std::cout << "AAA" << "BBB" ...
is almost always turned into multiple calls on the shared std::cout
object. Without external locking on your part, these are free to be interleaved in any way with other threads. Critically, this pretty much means the stream manipulators in iomanip
are impossible to use safely. Something like std::cout << std::hex << 123 << std::dec
will modify the stream globally, and some other thread that doesn't want hex
output might run at the wrong time and get it anyways. Furthermore, the fact that the stream formatting state and flags can change in the middle of an operation may produce some weird, wonderful or downright terrible results.
1 Certainly the source is available for open source runtimes like libstc++ (used by gcc) and libc++ (used by default by LLVM on most non-Linux platforms). Microsoft appears to be providing the source for their C runtime as well.
using std::cout in multiple threads
The threads are using different mutex
instances as the mutex
is a local variable in the exec()
function so locking the mutex
is pointless as each thread will be locking its own mutex
resulting in no synchronization between the threads. The same mutex
instance must be used by the threads to achieve synchronization.
To correct in the posted code, make the mutex
a member variable. However, if another Printer
object was created then there would be no synchronization between threads that used different Printer
instances. In this case, the mutex
would need to be a static
member variable to ensure synchronization:
class Printer
{
public:
//...
private:
static std::mutex mtx_;
};
std::mutex Printer::mtx_;
To ensure a mutex
is always released, regardless if a function exits normally or via an exception, use std:lock_guard
:
std::lock_guard<std::mutex> lock(m); // 'm' locked, and will be
// unlocked when 'lock' is destroyed.
std::cout<< "Hello " << std::this_thread::get_id() << std::endl;
std::chrono::milliseconds duration( 100 );
std::this_thread::sleep_for( duration );
Thread safety of std::cout insertion operator
I got the answer from Jonathan Wakely. Makes me feel rather stupid.
The difference is that on Fedora, libstdc++.so contains an explicit instantiation of the iostream classes. libstdc++.so isn't instrumented for ThreadSanitizer so it cannot detect any hazards related to it.
Related Topics
Pass by Pointer & Pass by Reference
Is There a Compact Equivalent to Python Range() in C++/Stl
Problems Using Member Function as Custom Deleter with Std::Shared_Ptr
Is There Some Ninja Trick to Make a Variable Constant After Its Declaration
Return Statement in Ternary Operator C++
What Difference Between Rand() and Random() Functions
Static Member Initialization for Specialized Template Class
What Is the Purpose of Ref-Qualified Member Functions
How to Easily Make Std::Cout Thread-Safe
Static VS Non-Static Variables in Namespace
_Attribute_((Weak)) and Static Libraries
Why Is There No 2-Byte Float and Does an Implementation Already Exist
Shared_From_This Causing Bad_Weak_Ptr