簡體   English   中英

在多線程程序中控制輸出流

[英]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
}

演示: http//coliru.stacked-crooked.com/a/362ecb660283cbff

好吧,好吧,如果您已經閱讀並理解了這些評論,您將發現真正的問題不是您想的那樣。 真正的問題是您的日志記錄代碼不是線程安全的。

這個答案很好地說明了問題。 盡管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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM