How to easily make std::cout thread-safe?

那年仲夏 提交于 2019-11-28 04:43:20

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:

  1. create a lock for the mutex, possibly blocking other threads
  2. do the output, i.e. do the operator << for the wrapped stream and the passed argument
  3. 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:

  1. synchronized_ostream::operator<< would aquire the lock
  2. synchronized_ostream::operator<< would delegate the printing of "Hello, " to cout
  3. operator<<(std::ostream&, const char*) would print "Hello, "
  4. synchronized_ostream::operator<< would construct a locked_ostream and pass the lock to that
  5. locked_ostream::operator<< would delegate the printing of name to cout
  6. operator<<(std::ostream&, std::string) would print the name
  7. The same delegation to cout happens for the exclamation point and the endline manipulator
  8. The locked_ostream temporary gets destructed, the lock is released
Nickolas George

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 sure \n
jumped of 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.

Conchylicultor

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.

Since C++20, you can use std::osyncstream wrapper:

http://en.cppreference.com/w/cpp/io/basic_osyncstream

{
  std::osyncstream bout(std::cout); // synchronized wrapper for std::cout
  bout << "Hello, ";
  bout << "World!";
  bout << std::endl; // flush is noted, but not yet performed
  bout << "and more!\n";
} // characters are transferred and std::cout is flushed

It provides the guarantee that all output made to the same final destination buffer (std::cout in the examples above) will be free of data races and will not be interleaved or garbled in any way, as long as every write to the that final destination buffer is made through (possibly different) instances of std::basic_osyncstream.

A feasible solution uses a line-buffer for each thread. You might get interleaved lines, but not interleaved characters. If you attach that to thread-local storage, you also avoid lock contention issues. Then, when a line is full (or on flush, if you want), you write it to stdout. This last operation of course has to use a lock. You stuff all this into a streambuffer, which you put between std::cout and it's original streambuffer.

The problem this doesn't solve is things like format flags (e.g. hex/dec/oct for numbers), which can sometimes percolate between threads, because they are attached to the stream. It's nothing bad, assuming you're only logging and not using it for important data. It helps to just not format things specially. If you need hex output for certain numbers, try this:

template<typename integer_type>
std::string hex(integer_type v)
{
    /* Notes:
    1. using showbase would still not show the 0x for a zero
    2. using (v + 0) converts an unsigned char to a type
       that is recognized as integer instead of as character */
    std::stringstream s;
    s << "0x" << std::setfill('0') << std::hex
        << std::setw(2 * sizeof v) << (v + 0);
    return s.str();
}

Similar approaches work for other formats as well.

For fast debugging c++11 applications and avoid interleaved output I just write small functions like these:

...
#include <mutex>
...
mutex m_screen;
...
void msg(char const * const message);
...
void msg(char const * const message)
{
  m_screen.lock();
  cout << message << endl;
  m_screen.unlock();
}

I use these types of functions for outputs and if numeric values are needed I just use something like this:

void msgInt(char const * const message, int const &value);
...
void msgInt(char const * const message, int const &value)
{
  m_screen.lock();
  cout << message << " = " << value << endl;
  m_screen.unlock();
}

This is easy and works fine to me, but I don't really know if it is technically correct. So I would be glad to hear your opinions.


Well, I didn't read this:

I don't want to search for each occurrence of std::cout and add a line of locking code.

I'm sorry. However I hope it helps somebody.

I know its an old question, but it helped me a lot with my problem. I created an utility class based on this post answers and I'd like to share my result.

Considering we use C++11 or latter C++ versions, this class provides print and println functions to compose strings before calling the standard output stream and avoid concurrency problems. These are variadic functions which use templates to print different data types.

You can check its use in a producer-consumer problem on my github: https://github.com/eloiluiz/threadsBar

So, here is my code:

class Console {
private:
    Console() = default;

    inline static void innerPrint(std::ostream &stream) {}

    template<typename Head, typename... Tail>
    inline static void innerPrint(std::ostream &stream, Head const head, Tail const ...tail) {
        stream << head;
        innerPrint(stream, tail...);
    }

public:
    template<typename Head, typename... Tail>
    inline static void print(Head const head, Tail const ...tail) {
        // Create a stream buffer
        std::stringbuf buffer;
        std::ostream stream(&buffer);
        // Feed input parameters to the stream object
        innerPrint(stream, head, tail...);
        // Print into console and flush
        std::cout << buffer.str();
    }

    template<typename Head, typename... Tail>
    inline static void println(Head const head, Tail const ...tail) {
        print(head, tail..., "\n");
    }
};

Along the lines of the answer suggested by Conchylicultor, but without inheriting from std::ostringstream:


EDIT: Fixed return type for the overloaded operator and added overload for std::endl.


EDIT 1: I have extended this into a simple header-only library for logging / debugging multi-threaded programs.


#include <iostream>
#include <mutex>
#include <thread>
#include <vector>    
#include <chrono>

static std::mutex mtx_cout;

// Asynchronous output
struct acout
{
        std::unique_lock<std::mutex> lk;
        acout()
            :
              lk(std::unique_lock<std::mutex>(mtx_cout))
        {

        }

        template<typename T>
        acout& operator<<(const T& _t)
        {
            std::cout << _t;
            return *this;
        }

        acout& operator<<(std::ostream& (*fp)(std::ostream&))
        {
            std::cout << fp;
            return *this;
        }
};

int main(void)
{


    std::vector<std::thread> workers_cout;
    std::vector<std::thread> workers_acout;

    size_t worker(0);
    size_t threads(5);


    std::cout << "With std::cout:" << std::endl;

    for (size_t i = 0; i < threads; ++i)
    {
        workers_cout.emplace_back([&]
        {
            std::cout << "\tThis is worker " << ++worker << " in thread "
                      << std::this_thread::get_id() << std::endl;
        });
    }
    for (auto& w : workers_cout)
    {
        w.join();
    }

    worker = 0;

    std::this_thread::sleep_for(std::chrono::seconds(2));

    std::cout << "\nWith acout():" << std::endl;

    for (size_t i = 0; i < threads; ++i)
    {
        workers_acout.emplace_back([&]
        {
            acout() << "\tThis is worker " << ++worker << " in thread "
                    << std::this_thread::get_id() << std::endl;
        });
    }
    for (auto& w : workers_acout)
    {
        w.join();
    }

    return 0;
}

Output:

With std::cout:
        This is worker 1 in thread 139911511856896
        This is worker  This is worker 3 in thread 139911495071488
        This is worker 4 in thread 139911486678784
2 in thread     This is worker 5 in thread 139911503464192139911478286080


With acout():
        This is worker 1 in thread 139911478286080
        This is worker 2 in thread 139911486678784
        This is worker 3 in thread 139911495071488
        This is worker 4 in thread 139911503464192
        This is worker 5 in thread 139911511856896

In addition to synchronisation this solution provides information about the thread from which the log was written.

DISCLAIMER: It's quite a naive way of syncing the logs, however it might be applicable for some small use cases for debugging.

thread_local int thread_id = -1;
std::atomic<int> thread_count;

struct CurrentThread {

  static void init() {
    if (thread_id == -1) {
      thread_id = thread_count++;
    }
  }

  friend std::ostream &operator<<(std::ostream &os, const CurrentThread &t) {
    os << "[Thread-" << thread_id << "] - ";
    return os;
  }
};

CurrentThread current_thread;
std::mutex io_lock;
#ifdef DEBUG
#define LOG(x) {CurrentThread::init(); std::unique_lock<std::mutex> lk(io_lock); cout << current_thread; x << endl;}
#else
#define LOG(x)
#endif

This can be used like this.

LOG(cout << "Waiting for some event");

And it will give log output

[Thread-1] - Entering critical section 
[Thread-2] - Waiting on mutex
[Thread-1] - Leaving critical section, unlocking the mutex
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!