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

Wol*_*ang 12 c++ multithreading valgrind openmp

我有通过cout和cerr写入控制台的OpenMP线程.这当然不安全,因为输出可以交错.我可以做点什么

#pragma omp critical(cerr)
{
   cerr << "my variable: " << variable << endl;
}
Run Code Online (Sandbox Code Playgroud)

如果可以用线程安全版本替换cerr会更好,类似于valgrind DRD手册中解释的方法(http://valgrind.org/docs/manual/drd-manual.html#drd-manual.effective- use)涉及从std :: ostreambuf派生一个类.理想情况下,最后我会用自己的线程cerr替换cerr,例如:

tcerr << "my variable: " << variable << endl;
Run Code Online (Sandbox Code Playgroud)

一旦遇到"endl",这样的类就可以打印到控制台.我不介意来自不同线程的行是否是交错的,但每行应仅来自一个线程.

我真的不明白C++中的所有这些流是如何工作的,它太复杂了.有没有人这样的课程或者可以告诉我如何为此目的创建这样的课程?

ste*_*fan 34

正如其他人所指出的,在C++ 11中,它std::cout 线程安全的.

但是,如果你喜欢它

std::cout << 1 << 2 << 3;
Run Code Online (Sandbox Code Playgroud)

对于不同的线程,输出仍然可以交错,因为每个<<都是一个新的函数调用,可以在另一个线程上进行任何函数调用.

为了避免交织#pragma omp critical-这将锁定一切-你可以做到以下几点:

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

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


Dav*_*eas 13

您可以使用类似于字符串构建器的方法.创建一个非模板类:

  • 提供模板operator<<插入此对象
  • 内部构建成 std::ostringstream
  • 在销毁时转储内容

粗略的方法:

 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());
    }
 };
Run Code Online (Sandbox Code Playgroud)

用于:

AtomicWriter() << "my variable: " << variable << "\n";
Run Code Online (Sandbox Code Playgroud)

或者在更复杂的场景中:

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

如果你想要操纵符,你需要添加更多的重载,你可以使用write比析构函数中fprintf原子写更好的,或者std::cerr,你可以进行泛化,以便将目标传递给构造函数(std::ostream/ file descriptor/FILE*),


And*_*ete 5

我没有足够的声誉发表评论,但我想将我添加到 AtomicWriter 类中以支持 std::endl 并允许使用除 std::cout 之外的其他流。这里是:

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(); }
};
Run Code Online (Sandbox Code Playgroud)


Som*_*ude 0

您可以通过继承来做到这一点std::basic_streambuf,并覆盖正确的函数以使其线程安全。然后将此类用于您的流对象。