如何制作支持`<<`操作的线程安全日志类?

Posted

技术标签:

【中文标题】如何制作支持`<<`操作的线程安全日志类?【英文标题】:How to make thread safe Log class that supports `<<` operations? 【发布时间】:2011-08-19 07:00:43 【问题描述】:

所以我有这样的日志类:

#include <iostream>
#include <sstream>
#include <boost/circular_buffer.hpp>
#include <boost/foreach.hpp>

class FlushInternal;

class Log

public:

    static FlushInternal* endl;

    Log(int log_length)
    
        i = 0;
        messages_buffer = new boost::circular_buffer<std::string>(log_length);
    

    template <class T>
    Log &operator<<(const T &v)
    
        current_message << v;
        return *this;
    

    Log &operator<<(std::ostream&(*f)(std::ostream&)) 
    
        current_message << *f;
        return *this;
    

    Log &operator<<(FlushInternal*)
    
        ++i;
        messages_buffer->push_back(current_message.str());
        clean_stringstream(current_message);
        is_filled();
        return *this;
    

    boost::circular_buffer<std::string> *messages_buffer;

private:
    int i;
    std::stringstream current_message;

    void is_filled()
    
        if (i >= messages_buffer->capacity())
        
            i = 0;
            BOOST_FOREACH(std::string s, *messages_buffer)
            
                std::cout << ++i << ": "  << s << " ;" << std::endl;
             
            i = 0;
        
    

    void clean_stringstream(std::stringstream &message)
    
        message.flush();
        message.clear();
        message.seekp(0);
        message.str("");
    
;

FlushInternal* Log::endl = 0;

我可以这样使用它:

#include <log.h>
int main()

    Log l(2);
    l << "message one: " << 1 << Log::endl;
    l << "message two:" << " " << 2 << Log::endl;
    l << "message " << "three: " << 3 << Log::endl;
    l << "message" << " "  << "four: " << 4 << Log::endl;
    std::cin.get();

这将输出:

1: message one: 1 ;
2: message two: 2 ;
1: message three: 3 ;
2: message four: 4 ;

如您所见,我可以在每条日志消息中添加任意数量的&lt;&lt;。我希望能够同时使用来自多个线程的Log 类的一个实例。所以我会有类似的东西(编译,运行但什么都不跟踪的伪代码。):

#include <boost/thread.hpp>
#include <log.h>

Log *l;

void fun_one()

    *l << "message one: " << 1 << Log::endl;
    *l << "message two:" << " " << 2 << Log::endl;


void fun_two()

    *l << "message " << "three: " << 3 << Log::endl;
    *l << "message" << " "  << "four: " << 4 << Log::endl;


int main()

    l = new Log(2);
    boost::thread(fun_one);
    boost::thread(fun_two);
    std::cin.get();

如您所见,我希望将消息插入到登录多线程函数中。我想知道 - 如何让我的日志 cclass 支持这个?

【问题讨论】:

我记得 Qt 有它的qDebug() 线程安全。也许你可以参考一下。 qDebug() 不是线程安全的 【参考方案1】:

由 ***foe 链接的方法几乎是规范的方法。基本上为最左边的

唯一的问题是这个蓄能器的确切机制。该示例使用ostringstream,但我已经看到ofstream 用于直接使用的日志文件(需要锁定以确保输出结束在一行上)。

在某些平台上创建ostringstreams 相对昂贵,因为它们可能需要锁定和复制一些内部语言环境相关的东西。您还可以为有趣的类型重新实现

一个有用的优化是在临时的构造点确定是否会发出跟踪(例如,是否在该特定级别启用跟踪),并且在这种情况下根本不创建临时的胆量 -所有的插入操作都是无操作的。

【讨论】:

【参考方案2】:

这是一种方法:

http://drdobbs.com/cpp/201804215

它基本上每次执行日志记录时都会创建一个新的ostringstream 对象,这使其成为线程安全的。我不能说我对此很热衷,因为这对我来说似乎有点笨拙。

您可能会查看 Qt 日志记录类,因为它们支持 &lt;&lt; operator,但我不确定线程​​安全性。

【讨论】:

以上是关于如何制作支持`<<`操作的线程安全日志类?的主要内容,如果未能解决你的问题,请参考以下文章

我可以制作一个线程安全的 std::atomic<vector<int>> 吗?

C++单例模式实现(线程安全&支持多参数构造)

python文件操作

通过 JVM 日志来进行安全点分析

golang笔记-日志小结

C++中的线程安全删除