在多个线程中使用std::cout
using std::cout in multiple threads
我写了一个简单的程序来测试c++11中的Thread,但std::cout
并没有像我预期的那样工作。
class Printer
{
public:
void exec()
{
mutex m;
m.lock();
cout<<"Hello "<<this_thread::get_id()<<endl;
chrono::milliseconds duration( 100 );
this_thread::sleep_for( duration );
m.unlock();
}
};
int main()
{
Printer printer;
thread firstThread([&printer](){
while(1)
printer.exec();
});
thread secondThread([&printer](){
while(1)
printer.exec();
});
firstThread.join();
secondThread.join();
}
一些结果:
Hello 11376
Hello 16076
Hello 16076
Hello Hello 11376
16076
Hello 11376
,....
我使用互斥锁来锁定线程,所以我不明白为什么两个线程同时执行std::cout
。这让我很困惑。有人能解释发生了什么吗!?!
线程使用不同的mutex
实例,因为mutex
是exec()
函数中的局部变量,所以锁定mutex
毫无意义,因为每个线程都将锁定自己的mutex
,导致线程之间没有同步。线程必须使用相同的mutex
实例才能实现同步。
要更正已发布的代码,请将mutex
作为成员变量。但是,如果创建了另一个Printer
对象,那么使用不同Printer
实例的线程之间就不会同步。在这种情况下,mutex
需要是static
成员变量以确保同步:
class Printer
{
public:
//...
private:
static std::mutex mtx_;
};
std::mutex Printer::mtx_;
为了确保mutex
始终被释放,无论函数是正常退出还是通过异常退出,请使用std:lock_guard
:
std::lock_guard<std::mutex> lock(m); // 'm' locked, and will be
// unlocked when 'lock' is destroyed.
std::cout<< "Hello " << std::this_thread::get_id() << std::endl;
std::chrono::milliseconds duration( 100 );
std::this_thread::sleep_for( duration );
接受的答案是正确的。然而,将关注点分开是很好的:
- 您需要一种以线程安全的方式打印到
std::cout
的方法 - 您需要创建对象/函数/函数来在线程中运行并启动它们
下面是我使用的一个实用程序,它只专注于收集std::cout
的参数,并在static std::mutex
:下将它们流式传输出去
#include <iostream>
#include <mutex>
std::ostream&
print_one(std::ostream& os)
{
return os;
}
template <class A0, class ...Args>
std::ostream&
print_one(std::ostream& os, const A0& a0, const Args& ...args)
{
os << a0;
return print_one(os, args...);
}
template <class ...Args>
std::ostream&
print(std::ostream& os, const Args& ...args)
{
return print_one(os, args...);
}
std::mutex&
get_cout_mutex()
{
static std::mutex m;
return m;
}
template <class ...Args>
std::ostream&
print(const Args& ...args)
{
std::lock_guard<std::mutex> _(get_cout_mutex());
return print(std::cout, args...);
}
此代码可以重用于std::cout
以外的流,但以上代码专门针对std::cout
。有了这个,你的Printer::exec()
现在可以显著简化:
void exec()
{
print("Hello ", std::this_thread::get_id(), 'n');
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
现在,您的Printer
不仅可以以线程安全的方式使用cout
,而且已经简化(例如,对于cout
不需要维护自己的mutex
),而且您的所有其他类型和函数也可以使用cout
,并且可以安全地进行互操作。print
函数本身现在维护mutex
,并且该事实被封装在print
的所有客户端之外。
我将分享Nicolás在这个问题中给出的技巧,我发现它比Howard Hinnant实现更优雅。其想法是创建一个临时的ostrinstream对象,并将保护代码放在析构函数上。
/** Thread safe cout class
* Exemple of use:
* PrintThread{} << "Hello world!" << std::endl;
*/
class PrintThread: public std::ostringstream
{
public:
PrintThread() = default;
~PrintThread()
{
std::lock_guard<std::mutex> guard(_mutexPrint);
std::cout << this->str();
}
private:
static std::mutex _mutexPrint;
};
std::mutex PrintThread::_mutexPrint{};
然后,您可以从任何线程将其用作常规std::cout
:
PrintThread{} << "val = " << 33 << std::endl;
该对象以常规std::ostringstream
的形式收集数据。一旦达到昏迷状态,物体就会被摧毁,并刷新所有收集到的信息。
您可以考虑全局std::mutex cout_mutex;
(在命名空间中的某个位置),它用于保护std::cout
输出。请确保使用std::lock<std::mutex>
(这样您就不会忘记解锁互斥锁并确保异常安全)。
- std::cout.imbue()多重调用
- 如何在C++中用std::cout正确显示带十六进制的字符串文本
- 为什么在C的循环中使用printf的Rust代码不显示输出,而在C++的循环中显示std::cout
- 为什么我应该在异常处理中使用std::cerr而不是std::cout
- std::cout输出int时出现编译错误
- 为什么 std::cout 打印浮点数、双精度和长双精度到相同的小数精度?
- main() 中的 std::cout 在调试期间不会在调试控制台中打印任何内容
- std::cout 来自多个线程
- 在 c++ 中以十六进制格式打印无符号字符(BYTE).使用 std::cout
- Cuda 基本程序 (将值写入矩阵和 std:cout 不起作用) ;主功能不启动
- 是否可以为 std::cout 创建别名?
- std::cout 在打印变量与函数表达式时的行为不同
- 带有 std::cout 的多线程控制台文本动画
- 捕获/禁止发送到 std::cout 的 OpenCV 警告
- printf() 和 std::cout 在指针方面的区别
- 使用字符串流加速 std::cout 日志记录
- 是否可以在每次使用std::cout时执行一个函数
- 为什么'std::cout << !+2 '输出 0?
- 如何将自定义类传递给std::cout
- 对于输出,std::copy是否比std::cout快