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

时间:2018-06-04 12:21:15

标签: c++ multithreading

我正在尝试控制模拟中的输出打印。它打印了很多输出流信息。这是我尝试控制输出流的示例代码。有时我想打印每个线程的信息,有时我不希望线程中的单个打印减少模拟中的系统调用。我传递命令行参数来控制流。参数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;
}

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

好吧,好吧,如果你已经阅读并理解了这些评论,你会发现真正的问题并不是你认为的那样。真正的问题是您的日志记录代码不是线程安全的。

This answer很好地解释了这个问题。虽然ostream本身就是线程安全的(因为C ++ 11),std::cout << "-:Thread 1:-" << std::endl;之类的内容实际上是对std::cout.operator<<两个调用,而另一个线程可能会介于两者之间他们因此扼杀你的输出。我想,你可以这样做。

因此,从this post无耻地窃取代码我谦卑地提交以下解决方案(也有一个全局标记,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运行。