2013-02-22 40 views
8

我有OpenMP线程通过cout和cerr写入控制台。这当然是不安全的,因为输出可以交错。我可以做类似多个线程写入std :: cout或std :: cerr

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

,类似的做法在Valgrind的DRD手册(​​3210)涉及从STD派生一个类解释说这将是更好,如果能有一个线程安全的版本替换CERR :: ostreambuf 。理想情况下,最后我会用我自己的螺纹cerr来代替cerr,例如简单地说:

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

这样的类只要遇到“endl”就可以打印到控制台。我不介意来自不同线程的线是否交错,但每条线只能来自一个线程。

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

+0

请不要暗示的printf ..;) – Wolfgang 2013-02-22 21:55:07

+0

*“这当然不是安全的” * - 这是不是在C++ 11真正的,除非你采取故意的动作,使其真正。 – 2013-02-22 21:56:38

+0

你的头衔说'cout'不是'cerr'。 – Barmar 2013-02-22 21:59:56

回答

0

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

8

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

  • 插入提供模板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()); 
    } 
}; 

作为:

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

或者在更复杂的情况:

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

您将需要添加更多的重载如果你想操纵器,您可以使用writefprintf更好地为在析构函数中原子写入,或std::cerr ,您可以概括以便将目标传递给构造函数(std::ostream /文件描述符/ FILE*),

+0

我想我还会添加一个'flush'成员,它与析构函数完全相同,并清除内部缓冲区。如果你愿意,你可以重复使用相同的原子。有些人可能更喜欢在第二个例子中使用额外的范围。 – 2013-02-22 22:59:11

+0

@MooingDuck:不知道要走什么路...我明白你要求什么,但是我发现范围允许我在查看逻辑而不是痕迹时忽略内容(我们的日志框架允许类似结构体)。也就是说,如果使用正确(即不要将逻辑与日志记录混合使用),范围可用于分析内容并确保没有真正的逻辑,之后我不需要尝试解释什么是内部循环我正在研究整个功能的逻辑。 – 2013-02-22 23:35:09

20

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

但是如果你使用它像

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

与不同的线程,输出仍然可以交错,因为每个<<是一个新的函数调用可以通过另一个线程的任何函数调用preceeded。

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

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

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

0

我没有足够的声望发表评论,但我想发布我的除了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(); } 
};