20

I have OpenMP threads that write to the console via cout and cerr. This of course is not safe, since output can be interleaved. I could do something like

#pragma omp critical(cerr)
{
   cerr << "my variable: " << variable << endl;
}

It would be nicer if could replace cerr with a thread-safe version, similar to the approach explained in the valgrind DRD manual (http://valgrind.org/docs/manual/drd-manual.html#drd-manual.effective-use) which involves deriving a class from std::ostreambuf. Ideally in the end I would just replace cerr with my own threaded cerr, e.g. simply:

tcerr << "my variable: " << variable << endl;

Such a class could print to the console as soon as it encounters an "endl". I do not mind if lines from different threads are interleaved, but each line should come only from one thread.

I do not really understand how all this streaming in C++ works, it is too complicated. Has anybody such a class or can show me how to create such a class for that purpose?

stefan
  • 10,215
  • 4
  • 49
  • 90
Wolfgang
  • 1,408
  • 2
  • 15
  • 20

5 Answers5

46

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.

stefan
  • 10,215
  • 4
  • 49
  • 90
  • Do you happen to have a source for only operator<< calls being interleaved, as opposed to the individual characters? https://stackoverflow.com/questions/6374264/is-cout-synchronized-thread-safe suggests that it is "safe" in the sense of not causing a race, but may still result in interleaving. – Cactus Golov Jun 01 '21 at 09:39
14

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*),

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I think I'd also add a `flush` member that does the same as the destructor and clears the internal buffer. Then you can reuse the same atomic over and over if you wish. Some people may prefer that to using extra scopes as in your second example. – Mooing Duck Feb 22 '13 at 22:59
  • @MooingDuck: Not sure what way to go... I understand what you ask for, but I find that the scope allows me to ignore the contents when I am looking at the logic and not the traces (our logging framework allows for similar constructs). That is, when used correctly (i.e. don't mix logic with logging), the scope can be used to analyze the contents and ensure that no real logic is there, after which I don't need to try to interpret what the internal loops are doing if I am looking at the logic of the whole function. – David Rodríguez - dribeas Feb 22 '13 at 23:35
  • IMO This hides too much interesting logic in the destructor; making it hard to understand the consuming code; as indicated by the comment that indicates where the interesting stuff happens. Adding flush seems like a good idea except then it has two modes: explicit (flush) and implicit and that's confusing. And adding more overloads sounds like re-inventing the wheel. Expose the stringstream; or maybe just use that instead of defining a new class that does basically the same thing. – steve Apr 10 '21 at 10:03
7

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(); }
};
András Fekete
  • 162
  • 1
  • 5
1

Put the following code in header file atomic_stream_macro.h

#ifndef atomic_stream_macro_h
#define atomic_stream_macro_h

#include <mutex>

/************************************************************************/
/************************************************************************/

extern std::mutex print_mutex;

#define PRINT_MSG(out,msg)                                           \
{                                                                    \
    std::unique_lock<std::mutex> lock (print_mutex);                 \
                                                                     \
    out << __FILE__ << "(" << __LINE__ << ")" << ": "                \
        << msg << std::endl;                                         \
}

/************************************************************************/
/************************************************************************/

#endif

Now the macro can be used from a file as follows.

#include <atomic_stream_macro.h>
#include <iostream>

int foo (void)
{
    PRINT_MSG (std::cout, "Some " << "Text " << "Here ");
}

Finally, in the main.cxx, declare the mutex.

#include <mutex>

std::mutex print_mutex;

int main (void)
{
    // launch threads from here
}
Krishnendu
  • 116
  • 4
0

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621