How to easily make std::cout thread-safe?
While I can't be sure this applies to every compiler / version of std libs
but in the code-base I'm using std::cout::operator<<()
it is already thread-safe.
I'm assuming that what you're really trying to do it stop std::cout
from mixing string when concatenating with the operator<<
multiple time per string, across multiple threads.
The reason strings get garbled is because there is a "External" race on the operator<<
this can lead to things like this happening.
//Thread 1
std::cout << "the quick brown fox " << "jumped over the lazy dog " << std::endl;
//Thread 2
std::cout << "my mother washes" << " seashells by the sea shore" << std::endl;
//Could just as easily print like this or any other crazy order.
my mother washes the quick brown fox seashells by the sea shore \n
jumped over the lazy dog \n
If that's the case there is a much simpler answer than making your own thread safe cout or implementing a lock to use with cout.
Simply compose your string before you pass it to cout
For example.
//There are other ways, but stringstream uses << just like cout..
std::stringstream msg;
msg << "Error:" << Err_num << ", " << ErrorString( Err_num ) << "\n";
std::cout << msg.str();
This way your stings can't be garbled because they are already fully formed, plus its also a better practice to fully form your strings anyway before dispatching them.
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
I really like the trick from Nicolás given in this question of creating a temporary object and putting the protection code on the destructor.
/** Thread safe cout class
* Exemple of use:
* PrintThread{} << "Hello world!" << std::endl;
*/
class PrintThread: public std::ostringstream
{
public:
PrintThread() = default;
~PrintThread()
{
std::lock_guard<std::mutex> guard(_mutexPrint);
std::cout << this->str();
}
private:
static std::mutex _mutexPrint;
};
std::mutex PrintThread::_mutexPrint{};
You can then use it as a regular std::cout
, from any thread:
PrintThread{} << "my_val=" << val << std::endl;
The object collect data as a regular ostringstream
. As soon the coma is reached, the object is destroyed and flush all collected information.