[英]Synchronizing STD cout output multi-thread
后來我一直在使用多線程編碼,在寫了一段時間之后,我意識到如果我在不同的boost :: threads中使用std :: cout,輸出將沒有邏輯順序,我正在測試的程序是就像是:
#include <boost/thread/thread.hpp>
#include <iostream>
int my01( void )
{
std::cout << "my01" << std::endl;
return 0;
}
/* my02, my03 and my04 are the same with different outputs*/
[...]
int main( void )
{
boost::thread t1(&my01);
boost::thread t2(&my02);
boost::thread t3(&my03);
boost::thread t4(&my04);
while(!t1.joinable() || !t2.joinable() || !t3.joinable() || !t4.joinable());
t1.join();
t2.join();
t3.join();
t4.join();
std::cout << "The end!" << std::endl;
getchar();
return 0;
}
輸出通常是(更改):
my02my01
my04
my03
空行
結束!
考慮到這個問題,我正在考慮創建一個線程來管理所有輸出,因此它們的順序如下:
my01
my02
my03
my04
結束!
編寫此類線程或管理這些輸出的最佳方法是哪種?
請也閱讀此問題的答案: cout是否同步/線程安全?
ps:我使用的是Visual C ++ 2010 Express,我的cpu有8個不同的內核。
感謝您的時間!
首先,您可以考慮避免所有顯式的線程管理,而是使用std::async
在任意數量的獨立線程中啟動任務。
其次,您不想創建線程本身中的I / O,而是要創建結果,並依次進行輸出。 這意味着線程函數僅創建一些數據,並將其留給調用者以將其實際寫出:
std::string process(int value) {
std::ostringstream buffer;
buffer << "my" << std::setfill('0') << std::setw(2) << value;
return buffer.str();
}
然后,我們需要異步啟動四個副本:
std::vector<std::future<std::string> > results;
for (int i=0; i<4; i++)
results.push_back(std::async(std::launch::async, process, i));
然后我們得到結果並按順序打印出來:
for (auto &r : results)
std::cout << r.get() << "\n";
將它們放在一起,我們可以獲得如下代碼:
#include <string>
#include <iostream>
#include <thread>
#include <future>
#include <sstream>
#include <vector>
#include <iomanip>
std::string process(int value) {
std::ostringstream buffer;
buffer << "my" << std::setfill('0') << std::setw(2) << value;
return buffer.str();
}
int main() {
std::vector<std::future<std::string>> rets;
for (int i=0; i<4; i++)
rets.push_back(std::async(std::launch::async, process, i));
for (auto & t : rets) {
t.wait();
std::cout << t.get() << "\n";
}
}
我要補充一點:我將其基於標准C ++ 11 future
。 我相信基本想法也應該與Boost future
一起使用(基於該標准),但我尚未對其進行測試。 我希望對Boost的期貨進行一些小的調整(例如,對名稱進行調整)。
我通過編寫一個瘦包裝器來解決它,該包裝器在開始寫入流時鎖定互斥鎖,並在寫入語句完成后將其釋放,並沖洗流。
用法:將std :: cout替換為safe_cout。
請記住,它不支持像std :: endl這樣的std :: cout功能。
請參閱下面的代碼或從此處獲取: https : //github.com/dkorolev/felicity/blob/master/safe_ostream.h
#include <cassert>
#include <iostream>
#include <mutex>
#include <memory>
struct safe_ostream {
struct guarded_impl {
guarded_impl() = delete;
guarded_impl(const guarded_impl&) = delete;
void operator=(const guarded_impl&) = delete;
guarded_impl(std::ostream& ostream, std::mutex& mutex) : ostream_(ostream), guard_(mutex) {
}
~guarded_impl() {
ostream_.flush();
}
template<typename T> void write(const T& x) {
ostream_ << x;
}
std::ostream& ostream_;
std::lock_guard<std::mutex> guard_;
};
struct impl {
impl() = delete;
void operator=(const impl&) = delete;
impl(std::ostream& ostream, std::mutex& mutex) : unique_impl_(new guarded_impl(ostream, mutex)) {
}
impl(const impl& rhs) {
assert(rhs.unique_impl_.get());
unique_impl_.swap(rhs.unique_impl_);
}
template<typename T> impl& operator<<(const T& x) {
guarded_impl* p = unique_impl_.get();
assert(p);
p->write(x);
return *this;
}
mutable std::unique_ptr<guarded_impl> unique_impl_;
};
explicit safe_ostream(std::ostream& ostream) : ostream_(ostream) {
}
template<typename T> impl operator<<(const T& x) {
return impl(ostream_, mutex_) << x;
}
std::ostream& ostream_;
std::mutex mutex_;
};
safe_ostream safe_cout(std::cout);
safe_ostream safe_cerr(std::cerr);
您可能需要在線程上強加一個順序,以便輸出的順序是您想要的(也許通過將線程實例或事件傳遞給適當的線程,以便它們只能按您的順序執行),或者您可以給所有輸出都具有一個線程序列號,將所有輸出排隊到一個“打印”線程中,並在該線程中保留所有亂序行的列表,以便根據需要進行打印。
在“真實”應用程序的情況下(即不是濫用線程的瑣碎的測試應用程序),其中線程在必須保留順序的順序緩沖區上並行執行大量工作,從而迫使線程彼此等待通常不是一個合理的選擇。 通常使用序列號,然后重新組合緩沖流。
給每個線程一個std::ostringstream
以寫入輸出。 在程序末尾,按順序打印每個線程的輸出。
考慮到線程4可能比線程1早完成,您將如何做?
使用鎖定。 如果可以使用升壓,請執行例如
int my01(boost::mutex *coutGuard)
{
{
// lock cout until the closing brace
boost::mutex::scoped_lock lock(*coutGuard);
std::cout << "my01" << std::endl;
}
return 0;
}
int main( void )
{
boost::mutex coutGuard;
boost::thread t1(boost::bind(&my01, &coutGuard));
...
}
代替scoped_lock
,可以使用lock_guard
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.