[英]Controlling output stream in multi-threaded program
我試圖在模擬中控制輸出打印。 它打印很多輸出流信息。 這是我如何控制輸出流的示例代碼。 有時我想為每個線程打印信息,有時又不想從線程中打印任何內容以減少模擬中的系統調用。 我通過命令行參數來控制流。 參數v
表示不打印。 問題是它在整個模擬器中需要很多if
語句。 有什么簡單的方法可以解決這個問題?
#include <iostream>
#include <thread>
void work_to_do_1(char ch)
{
//work for thread 1
if(ch != 'v')
std::cout << "-:Thread 1:-" << std::endl;
}
void work_to_do_2(char ch)
{
if (ch != 'v')
std::cout << "-:Thread 2:-" << std::endl;
}
void work_to_do_3(char ch)
{
if (ch != 'v')
std::cout << "-:Thread 3:-" << std::endl;
}
int main(int argc, char *arg[])
{
std::cout << "You have entered " << argc
<< " arguments:" << "\n";
for (int i = 0; i < argc; ++i)
{
std::cout << arg[i] << "\n";
}
char t = *arg[1];
std::cout << "manager is running" << std::endl;
std::thread t1(work_to_do_1, t);
std::thread t2(work_to_do_2, t);
std::thread t3(work_to_do_3, t);
t1.join();
t2.join();
t3.join();
system("pause");
return 0;
}
制作自己的nul流:
struct cnul_t : std::basic_ostream<char> {} cnul;
template<class T> std::ostream& operator<<(cnul_t& os, T const&) { return os; }
並將您的輸出重定向到它以忽略它:
#include <ostream>
#include <iostream>
struct cnul_t : std::basic_ostream<char> {} cnul;
template<class T> std::ostream& operator<<(cnul_t& os, T const&) { return os; }
void maybe_log(bool b)
{
std::ostream& out = b == true ? std::cout : cnul;
out << "Hello, World!\n";
}
int main()
{
maybe_log(true); // outputs Hello, World!
maybe_log(false); // no output
}
好吧,好吧,如果您已經閱讀並理解了這些評論,您將發現真正的問題不是您想的那樣。 真正的問題是您的日志記錄代碼不是線程安全的。
這個答案很好地說明了問題。 盡管ostream
本身具有線程安全性(自C ++ 11起),但類似std::cout << "-:Thread 1:-" << std::endl;
實際上是對std::cout.operator<<
兩次調用,並且另一個線程可能會潛入它們之間,從而使您的輸出變暗。 我想這是您可以做到的。
因此,我毫不羞恥地從本文中竊取了代碼,我謙虛地提交了以下解決方案(該解決方案還具有全局標志gLogging
,用於打開或關閉日志記錄)。 每當您登錄std::endl
時,它將自動向std :: cout寫入行。 我將其寫為練習來發展自己的個人技能,我認為您可能希望擁有它。
有關如何檢測到std::endl
的解釋,請參見鏈接的帖子,但是基本原理是每個線程都有一個單獨的日志緩沖區,當它有完整的輸出行要清除時,將刷新到std::cout
。 該代碼包括一個管理器類( Logger
),用於處理創建,銷毀和訪問這些緩沖區的細節。 您只需要在每個線程的開頭放置兩行初始化代碼,如圖所示,然后登錄到logstream
而不是std::cout
。
#include <iostream>
#include <sstream>
#include <mutex>
#include <map>
#include <thread>
bool gLogging = true;
constexpr int bufsize = 512; // needs to be big enough for longest logging line expected
// A streambuf that writes atomically to std::cout when (indirectly) it sees std::endl
class LogBuf : public std::stringbuf
{
public:
LogBuf () { setbuf (m_buf = new char [bufsize], bufsize); str (""); }
~LogBuf () { delete [] m_buf; }
protected:
// This gets called when the ostream we are serving sees endl
int sync() override
{
if (gLogging)
{
std::cout << str();
std::cout.flush();
}
str("");
return 0;
}
private:
char *m_buf;
};
// An ostream that uses LogBuf
class LogStream : public std::ostream
{
public:
LogStream () : std::ostream (m_LogBuf = new LogBuf ()) { }
~LogStream () { delete m_LogBuf; }
private:
LogBuf *m_LogBuf;
};
// A class to manage LogStream objects (one per thread)
class Logger
{
public:
void AddThread (void)
{
mutex.lock ();
m_logstreams [std::this_thread::get_id ()] = new LogStream ();
mutex.unlock ();
}
void RemoveThread ()
{
mutex.lock ();
std::thread::id thread_id = std::this_thread::get_id ();
LogStream *logstream = m_logstreams [thread_id];
m_logstreams.erase (m_logstreams.find (thread_id));
mutex.unlock ();
delete logstream;
}
LogStream& GetLogStream ()
{
mutex.lock ();
LogStream *logstream = m_logstreams [std::this_thread::get_id ()];
mutex.unlock ();
return *logstream;
}
private:
static std::mutex mutex;
std::map<const std::thread::id, LogStream *> m_logstreams;
};
std::mutex Logger::mutex;
Logger logger;
// A simple class to make sure we remember to call RemoveThread
class LogStreamHelper
{
public:
LogStreamHelper () { logger.AddThread (); }
~LogStreamHelper () { logger.RemoveThread (); }
inline LogStream &GetLogStream () { return logger.GetLogStream (); }
};
// Test program
void work_to_do_1()
{
LogStreamHelper logstream_helper;
LogStream& logstream = logstream_helper.GetLogStream ();
logstream << "-:Thread 1:-" << std::endl;
}
void work_to_do_2()
{
LogStreamHelper logstream_helper;
LogStream& logstream = logstream_helper.GetLogStream ();
logstream << "-:Thread 2:-" << std::endl;
}
int main ()
{
LogStreamHelper logstream_helper;
LogStream& logstream = logstream_helper.GetLogStream ();
logstream << "Main thread" << std::endl;
std::thread t1 (work_to_do_1);
std::thread t2 (work_to_do_2);
t1.join ();
t2.join ();
return 0;
}
輸出:
Main thread
-:Thread 1:-
-:Thread 2:-
在Wandbox上運行它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.