简体   繁体   English

多个线程写入 std::cout 或 std::cerr

[英]multiple threads writing to std::cout or std::cerr

I have OpenMP threads that write to the console via cout and cerr.我有通过 cout 和 cerr 写入控制台的 OpenMP 线程。 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.如果可以用线程安全版本替换 cerr 会更好,类似于 valgrind DRD 手册中解释的方法( http://valgrind.org/docs/manual/drd-manual.html#drd-manual.effective- use ),它涉及从 std::ostreambuf 派生一个类。 Ideally in the end I would just replace cerr with my own threaded cerr, eg simply:理想情况下,最后我会用我自己的线程 cerr 替换 cerr,例如:

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

Such a class could print to the console as soon as it encounters an "endl".这样的类一旦遇到“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.我真的不明白 C++ 中的所有这些流是如何工作的,它太复杂了。 Has anybody such a class or can show me how to create such a class for that purpose?有没有人这样的课程或可以告诉我如何为此目的创建这样的课程?

As others pointed out, in C++11, std::cout is thread-safe. 正如其他人所指出的,在C ++ 11中, std::cout 线程安全的。

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: 为了避免在没有 #pragma omp critical 情况下进行交错 - 这将锁定所有内容 - 您可以执行以下操作:

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. 将123写入流的三个调用仅在一个线程中发生到本地非共享对象,因此不受任何其他线程的影响。 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. 然后,只有一个对共享输出流std::cout调用,其中项目123的顺序已经固定,因此不会搞砸。

You can use an approach similar to a string builder. 您可以使用类似于字符串构建器的方法。 Create a non-template class that: 创建一个非模板类:

  • offers templated operator<< for insertion into this object 提供模板化operator<<以插入此对象
  • internally builds into a std::ostringstream 内部构建为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* ), 您将需要添加更多的重载如果你想操纵器,您可以用write优于fprintf在析构函数中的原子写 ,或std::cerr ,你可以概括,以使目标被传递给构造( 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. 我没有足够的声誉来发表评论,但我想将我的添加内容发布到AtomicWriter类以支持std :: endl并允许除了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. 您可以通过继承std::basic_streambuf来完成它,并覆盖正确的函数以使其成为线程安全的。 Then use this class for your stream objects. 然后将此类用于流对象。

Put the following code in header file atomic_stream_macro.h<\/code>将以下代码放入头文件atomic_stream_macro.h<\/code>

#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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM