18

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

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

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

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

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

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

4

5 回答 5

42

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

但是,如果您像这样使用它

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

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

为了避免没有a 的交错#pragma omp critical- 这会锁定所有内容 - 您可以执行以下操作:

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

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

于 2013-02-22T22:52:02.153 回答
14

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

  • 提供模板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

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

于 2013-02-22T22:42:42.853 回答
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(); }
};
于 2015-12-21T20:07:49.997 回答
0

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

于 2013-02-22T22:00:52.777 回答
0

将以下代码放入头文件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

现在可以从文件中使用宏,如下所示。

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

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

最后,在 main.cxx 中,声明互斥锁。

#include <mutex>

std::mutex print_mutex;

int main (void)
{
    // launch threads from here
}
于 2022-02-01T13:39:30.067 回答