[英]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
實例才能實現同步。
要更正已發布的代碼,請將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
客戶端的位置。
我正在分享尼古拉斯在這個問題中給出的伎倆,我發現它比Howard Hinnant的實施更優雅。 我們的想法是創建一個臨時的ostringstream對象並將保護代碼放在析構函數上。
/** 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>
(這樣你就不會忘記解鎖互斥鎖和異常安全)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.