简体   繁体   English

在多线程程序中控制输出流

[英]Controlling output stream in multi-threaded program

I am trying to control the output prints in my simulation. 我试图在模拟中控制输出打印。 It prints a lot of output stream information. 它打印很多输出流信息。 This is a sample code of how I try to control the output stream. 这是我如何控制输出流的示例代码。 Sometimes I want to print information for each thread and sometimes I do not want a single print from threads to reduce the system calls in the simulation. 有时我想为每个线程打印信息,有时又不想从线程中打印任何内容以减少模拟中的系统调用。 I pass command line argument to control the stream. 我通过命令行参数来控制流。 Argument v means no prints. 参数v表示不打印。 The problem is it requires a lot of if statements in whole simulator. 问题是它在整个模拟器中需要很多if语句。 Is there any easy way to deal with this issue? 有什么简单的方法可以解决这个问题?

#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;
}

Make your own nul stream: 制作自己的nul流:

struct cnul_t : std::basic_ostream<char> {} cnul;
template<class T> std::ostream& operator<<(cnul_t& os, T const&) { return os; }

And redirect your output to it to ignore it: 并将您的输出重定向到它以忽略它:

#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
}

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

OK, well, if you have read and understood the comments you will see that the real problem is not what you think it is. 好吧,好吧,如果您已经阅读并理解了这些评论,您将发现真正的问题不是您想的那样。 The real problem is that your logging code is not threadsafe. 真正的问题是您的日志记录代码不是线程安全的。

This answer explains the problem very well. 这个答案很好地说明了问题。 Although ostream s are threadsafe in themselves (since C++11), something like std::cout << "-:Thread 1:-" << std::endl; 尽管ostream本身具有线程安全性(自C ++ 11起),但类似std::cout << "-:Thread 1:-" << std::endl; is actually two calls to std::cout.operator<< and another thread might sneak in between them thus garbling your output. 实际上是对std::cout.operator<< 两次调用,并且另一个线程可能会潜入它们之间,从而使您的输出变暗。 This, I imagine, you could do without. 我想这是您可以做到的。

So, stealing code unashamedly from this post I humbly submit the following solution (which also has a global flag, gLogging , to turn logging on or off). 因此,我毫不羞耻地从本文中窃取了代码,我谦虚地提交了以下解决方案(该解决方案还具有全局标志gLogging ,用于打开或关闭日志记录)。 This will write lines to std::cout atomically whenever you log std::endl . 每当您登录std::endl时,它将自动向std :: cout写入行。 I wrote this as an exercise to develop my own personal skills and I thought you might like to have it. 我将其写为练习来发展自己的个人技能,我认为您可能希望拥有它。

See the linked post for an explanation of how std::endl is detected, but the underlying principle is a separate log buffer for each thread which is flushed to std::cout when it has a complete line of output to get rid of. 有关如何检测到std::endl的解释,请参见链接的帖子,但是基本原理是每个线程都有一个单独的日志缓冲区,当它有完整的输出行要清除时,将刷新到std::cout The code includes a manager class ( Logger ) to take care of the details of creating, destroying and accessing these buffers. 该代码包括一个管理器类( Logger ),用于处理创建,销毁和访问这些缓冲区的细节。 You just need to put two lines of initialisation code at the start of each thread as shown and then log to logstream rather than std::cout . 您只需要在每个线程的开头放置两行初始化代码,如图所示,然后登录到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;
}

Output: 输出:

Main thread
-:Thread 1:-
-:Thread 2:-

Run it at Wandbox . Wandbox上运行它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM