Reputation: 1488
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?
Upvotes: 20
Views: 13343
Reputation: 126
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
}
Upvotes: 1
Reputation: 10355
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.
Upvotes: 48
Reputation: 172
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(); }
};
Upvotes: 7
Reputation: 208406
You can use an approach similar to a string builder. Create a non-template class that:
operator<<
for insertion into this objectstd::ostringstream
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*
),
Upvotes: 14
Reputation: 409356
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.
Upvotes: 0