multiple threads writing to std::cout or std::cerr

独自空忆成欢 提交于 2019-11-30 08:44:48

As others pointed out, in C++11, std::cout is thread-safe.

However if you use it like

std::cout << 1 << 2 << 3;

with different threads, the output can still be interleaved, since every << is a new function call which can be preceeded by any function call on another thread.

To avoid interleaving without a #pragma omp critical - which would lock everything - you can do the following:

std::stringstream stream; // #include <sstream> for this
stream << 1 << 2 << 3;
std::cout << stream.str();

The three calls writing 123 to the stream are happening in only one thread to a local, non-shared object, therefore aren't affected by any other threads. Then, there is only one call to the shared output stream std::cout, where the order of items 123 is already fixed, therefore won't get messed up.

You can use an approach similar to a string builder. Create a non-template class that:

  • offers templated operator<< for insertion into this object
  • internally builds into a std::ostringstream
  • dumps the contents on destruction

Rough approach:

 class AtomicWriter {
    std::ostringstream st;
 public:
    template <typename T> 
    AtomicWriter& operator<<(T const& t) {
       st << t;
       return *this;
    }
    ~AtomicWriter() {
       std::string s = st.str();
       std::cerr << s;
       //fprintf(stderr,"%s", s.c_str());
       // write(2,s.c_str(),s.size());
    }
 };

Use as:

AtomicWriter() << "my variable: " << variable << "\n";

Or in more complex scenarios:

{
   AtomicWriter w;
   w << "my variables:";
   for (auto & v : vars) {
      w << ' ' << v;
   }
}  // now it dumps

You will need to add more overloads if you want manipulators, you can use write better than fprintf for the atomic write in the destructor, or std::cerr, you can generalize so that the destination is passed to the constructor (std::ostream/file descriptor/FILE*),

I don't have enough reputation to post a comment, but I wanted to post my addition to the AtomicWriter class to support std::endl and allow for other streams to be used besides std::cout. Here it is:

class AtomicWriter {
    std::ostringstream st;
    std::ostream &stream;
public:
    AtomicWriter(std::ostream &s=std::cout):stream(s) { }
    template <typename T>
    AtomicWriter& operator<<(T const& t) {
        st << t;
        return *this;
    }
    AtomicWriter& operator<<( std::ostream&(*f)(std::ostream&) ) {
        st << f;
        return *this;
    }
    ~AtomicWriter() { stream << st.str(); }
};

You could do it by inheriting std::basic_streambuf, and override the correct functions to make it threadsafe. Then use this class for your stream objects.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!