Oli*_*mon 7 c++ logging multithreading asynchronous thread-safety
我实际上正在寻找一种在C++中进行异步和线程安全日志记录的方法.
我已经探索了线程安全的日志记录解决方案,如log4cpp,log4cxx,Boost:log或rlog,但似乎所有这些都使用了互斥锁.据我所知,互斥是一种同步解决方案,这意味着所有线程在尝试编写消息时都会被锁定,而其他线程则会被锁定.
你知道解决方案吗?
我认为您的陈述是错误的:使用互斥量不一定等同于同步解决方案.是的,Mutex用于同步控制,但它可以用于许多不同的事情.我们可以在例如生产者消费者队列中使用互斥,而日志记录仍然是异步发生的.
老实说,我没有研究过这些日志库的实现,但是应该可以创建一个异步appender(对于像log这样的log4j),logger写入生成器使用者队列,另一个工作线程负责写入文件(或者甚至委托另一个appender),以防它没有提供.
编辑:刚刚在log4cxx中进行了一次简短的扫描,它确实提供了一个AsyncAppender来完成我的建议:缓冲传入的日志记录事件,并异步委托给附加的appender.
我建议只使用一个线程进行日志记录来避免这个问题.为了将必要的数据传递给日志,您可以使用无锁的fifo队列(线程安全,只要生产者和消费者被严格分开,并且每个角色只有一个线程 - 因此每个生产者需要一个队列.)
包括快速无锁队列的示例:
queue.h:
#ifndef QUEUE_H
#define QUEUE_H
template<typename T> class Queue
{
public:
    virtual void Enqueue(const T &element) = 0;
    virtual T Dequeue() = 0;
    virtual bool Empty() = 0;
};
hybridqueue.h:
#ifndef HYBRIDQUEUE_H
#define HYBRIDQUEUE_H
#include "queue.h"
template <typename T, int size> class HybridQueue : public Queue<T>
{
public:
    virtual bool Empty();
    virtual T Dequeue();
    virtual void Enqueue(const T& element);
    HybridQueue();
    virtual ~HybridQueue();
private:
    struct ItemList
    {
        int start;
        T list[size];
        int end;
        ItemList volatile * volatile next;
    };
    ItemList volatile * volatile start;
    char filler[256];
    ItemList volatile * volatile end;
};
/**
 * Implementation
 * 
 */
#include <stdio.h>
template <typename T, int size> bool HybridQueue<T, size>::Empty()
{
    return (this->start == this->end) && (this->start->start == this->start->end);
}
template <typename T, int size> T HybridQueue<T, size>::Dequeue()
{
    if(this->Empty())
    {
        return NULL;
    }
    if(this->start->start >= size)
    {
        ItemList volatile * volatile old;
        old = this->start;
        this->start = this->start->next;
            delete old;
    }
    T tmp;
    tmp = this->start->list[this->start->start];
    this->start->start++;
    return tmp;
}
template <typename T, int size> void HybridQueue<T, size>::Enqueue(const T& element)
{
    if(this->end->end >= size) {
        this->end->next = new ItemList();
        this->end->next->start = 0;
        this->end->next->list[0] = element;
        this->end->next->end = 1;
        this->end = this->end->next;
    }
    else
    {
        this->end->list[this->end->end] = element;
        this->end->end++;
    }
}
template <typename T, int size> HybridQueue<T, size>::HybridQueue()
{
    this->start = this->end = new ItemList();
    this->start->start = this->start->end = 0;
}
template <typename T, int size> HybridQueue<T, size>::~HybridQueue()
{
}
#endif // HYBRIDQUEUE_H
如果我的问题得到了解决,您就会担心在记录器的关键部分进行I/O操作(可能写入文件).
Boost:log允许您定义自定义编写器对象.您可以定义operator()来调用异步I/O或将消息传递给日志记录线程(正在执行I/O).
http://www.torjo.com/log2/doc/html/workflow.html#workflow_2b
在Windows程序中,我们使用用户定义的Windows消息。首先,为堆上的日志条目分配内存。然后调用PostMessage,指针作为LPARAM,记录大小作为WPARAM。接收器窗口提取记录,显示它,并将其保存在日志文件中。然后PostMessage返回,并且分配的内存被发送者释放。这种方法是线程安全的,并且您不必使用互斥体。并发是由Windows的消息队列机制来处理的。不是很优雅,但很有效。
| 归档时间: | 
 | 
| 查看次数: | 7883 次 | 
| 最近记录: |