我知道,为了避免输出混合,多线程对cout和cerr的访问必须同步。在同时使用cout和cerr的程序中,单独锁定它们是否足够?或者同时写入cout和cerr仍然是不安全的?
编辑澄清:我知道cout和cerr在C++11中是“线程安全的”。我的问题是,不同线程同时对cout和cerr的写入是否会像两次对cout的写入那样相互干扰(导致交错输入等)。
发布于 2016-03-15 13:48:38
这里已经有了几个答案。我将总结并讨论它们之间的交互。
通常,
std::cout
和std::cerr
通常会集中到一个文本流中,因此在最有用的程序中,将它们锁定在一起会产生共同的结果。
如果忽略这个问题,cout
和cerr
在默认情况下会作为它们的stdio
对等项的别名,这些对等项是线程安全的as in POSIX,直到标准I/O函数(C++14§27.4.1/4,这比单独使用C更有保证)。如果您坚持选择这种函数,您会得到垃圾I/O,但不会得到未定义的行为(这是语言律师可能会将其与“线程安全”联系在一起,而不考虑其有用性)。
但是,请注意,尽管标准格式化I/O函数(例如读取和写入数字)是线程安全的,但用于更改格式的操作器(例如用于十六进制的std::hex
或用于限制输入字符串大小的std::setw
)不是线程安全的。因此,人们通常不能假设省略锁是安全的。
如果您选择单独锁定它们,事情会更加复杂。
单独锁定
为了提高性能,可以通过分别锁定cout
和cerr
来减少锁争用。它们是单独缓冲(或不缓冲)的,它们可能会刷新到单独的文件中。
默认情况下,cerr
会在每次操作之前刷新cout
,因为它们是“绑定的”。这会破坏分离和锁定,所以记得在使用cerr.tie( nullptr )
之前调用它。(这同样适用于cin
,但不适用于clog
。)
与stdio
解耦
该标准说,在cout
和cerr
上的操作不会引入竞争,但这不是它的确切含义。流对象并不特殊;它们的底层streambuf
缓冲区才是特殊的。
此外,调用std::ios_base::sync_with_stdio
的目的是删除标准流的特殊方面-允许它们像其他流一样进行缓冲。尽管该标准没有提到sync_with_stdio
对数据竞争的任何影响,但快速浏览一下libstdc++和libc++ (GCC和Clang) std::basic_streambuf
类就会发现,它们不使用原子变量,因此在用于缓冲时,它们可能会创建竞争条件。(另一方面,libc++ sync_with_stdio
实际上什么也不做,所以调用它也没关系。)
如果您希望在不考虑锁定的情况下获得额外的性能,那么sync_with_stdio(false)
是一个好主意。然而,在这样做之后,锁定是必要的,如果锁是独立的,则还需要cerr.tie( nullptr )
。
发布于 2018-06-03 06:40:35
这可能是有用的;)
inline static void log(std::string const &format, ...) {
static std::mutex locker;
std::lock_guard<std::mutex>(locker);
va_list list;
va_start(list, format);
vfprintf(stderr, format.c_str(), list);
va_end(list);
}
发布于 2019-02-07 01:01:47
我使用的是这样的东西:
// Wrap a mutex around cerr so multiple threads don't overlap output
// USAGE:
// LockedLog() << a << b << c;
//
class LockedLog {
public:
LockedLog() { m_mutex.lock(); }
~LockedLog() { *m_ostr << std::endl; m_mutex.unlock(); }
template <class T>
LockedLog &operator << (const T &msg)
{
*m_ostr << msg;
return *this;
}
private:
static std::ostream *m_ostr;
static std::mutex m_mutex;
};
std::mutex LockedLog::m_mutex;
std::ostream* LockedLog::m_ostr = &std::cerr;
https://stackoverflow.com/questions/14637595
复制相似问题