前言
std::cout
重载了<<
运算符,这使得写一些很短的代码时很方便。但是如果在多线程的条件下,cout并不是线程安全的。
举例
举个例子,我们创建5个线程
#include <iostream>
#include <thread>
void Test() {
std::cout << "msg1"
<< " msg2"
<< " msg3"
<< " thread_id = " << std::this_thread::get_id() << std::endl;
}
int main() {
std::thread threads[5];
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(Test);
}
for (int i = 0; i < 5; i++) {
threads[i].join();
}
}
实际上,看样子好像控制台应该输出5行内容,但是运行结果可能是这样的
msg1 msg2 msg3 thread_id = msg1 msg2 msg3 thread_id = 139926598575808139926606968512
msg1 msg2 msg3 thread_id = 139926590183104
msg1 msg2 msg3 thread_id = 139926455965376
msg1 msg2 msg3 thread_id = 139926581790400
这是因为cout在使用时可能会存在线程之间的打印信息乱串的问题,看一下编译器眼中我们这段程序中的cout是什么样的:
std::operator<<(std::operator<<(std::operator<<(std::operator<<(std::operator<<(std::cout, "msg1"), " msg2"), " msg3"), " thread_id = "), std::this_thread::get_id()).operator<<(std::endl);
可以看到,这不是通过单个 std::operator<<
调用完成的,也就是说这个操作并不是原子的
解决方法
- 使用
std::format
(C++20) - 使用第三方库,如
folly
,fmtlib
等 - 使用stringstream
- ……
这里使用stringstream做个演示。将Test函数修改如下:
std::stringstream ss;
ss << "msg1"
<< " msg2"
<< " msg3"
<< " thread_id = " << std::this_thread::get_id() << std::endl;
std::cout << ss.str();
这样,控制台的输出就不会乱串了。