iostream线程安全,必须cout和cerr分开锁定?

zap*_*oyd 10 c++ cout thread-safety

我理解为了避免输出混合,必须同步多个线程对cout和cerr的访问.在同时使用cout和cerr的程序中,单独锁定它们是否足够?或者同时写cout和cerr仍然不安全?

编辑说明:我知道cout和cerr在C++ 11中是"线程安全的".我的问题是,对cout的写入和对不同线程的cerr的写入是否会以两次写入cout的方式相互干扰(导致交错输入等).

Pet*_*ker 12

如果执行此功能:

void f() {
    std::cout << "Hello, " << "world!\n";
}
Run Code Online (Sandbox Code Playgroud)

从多个线程中,您将获得两个字符串的或多或少的随机交错,"Hello, "并且"world\n".那是因为有两个函数调用,就像你编写了这样的代码:

void f() {
    std::cout << "Hello, ";
    std::cout << "world!\n";
}
Run Code Online (Sandbox Code Playgroud)

要防止交错,您必须添加一个锁:

std::mutex mtx;
void f() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Hello, " << "world!\n";
}
Run Code Online (Sandbox Code Playgroud)

也就是说,交错的问题有没有关系cout.它是关于使用它的代码:有两个单独的函数调用插入文本,所以除非你阻止多个线程同时执行相同的代码,否则在函数调用之间有一个线程切换的可能性,这就是给你的交错.

请注意,互斥锁不会阻止线程切换.在前面的代码片段中,它阻止了从两个线程同时执行内容f(); 其中一个线程必须等到另一个完成.

如果你也在cerr,你有同样的问题,你会得到交错输出,除非你确保你没有两个线程同时进行这些插入函数调用,这意味着两个函数必须使用相同的互斥:

std::mutex mtx;
void f() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Hello, " << "world!\n";
}

void g() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cerr << "Hello, " << "world!\n";
}
Run Code Online (Sandbox Code Playgroud)


And*_*owl 7

在C++ 11,不像在C++ 03中,插入到和提取从全球流对象(cout,cin,cerr,和clog)是线程安全.无需提供手动同步.但是,有可能由不同线程插入的字符在输出时会不可预测地交错; 类似地,当多个线程从标准输入读取时,哪个线程将读取哪个令牌是不可预测的.

默认情况下,全局流对象的线程安全性处于活动状态,但可以通过调用sync_with_stdio流对象的成员函数并false作为参数传递来关闭它.在这种情况下,您必须手动处理同步.


Nor*_*rtM 5

同时写入cout和cerr 可能是不安全的!它取决于将cout 绑定到cerr还是不绑定。参见std :: ios :: tie

“绑定流是一个输出流对象,该对象在每次I / O操作之前都会被刷新。”

这意味着,写入cerr的线程可能会无意中调用cout.flush()。我花了一些时间来弄清楚,这就是我的一个项目中cout的输出中随机丢失行尾的原因:(

使用C ++ 98时,cout不应该与cerr捆绑在一起。但是尽管有标准,但在使用MSVC 2008时还是有困难的(我的经验)。使用以下代码时,一切工作正常。

std::ostream *cerr_tied_to = cerr.tie();
if (cerr_tied_to) {
    if (cerr_tied_to == &cout) {
        cerr << "DBG: cerr is tied to cout ! -- untying ..." << endl;
        cerr.tie(0);
    }
}
Run Code Online (Sandbox Code Playgroud)

另请参阅:为什么cerr刷新cout的缓冲区