C++ 如何确保链式日志语句是原子的?

C++ 如何确保链式日志语句是原子的?,c++,multithreading,method-chaining,C++,Multithreading,Method Chaining,我有一个日志类,它有操作符一种方法是使用宏,即 #define LOG(x) \ {\ <acquire scoped lock> \ oLogger << x; \ } #定义日志(x)\ {\ \ oLogger由于我必须将日志国际化,我更喜欢以下内容: oLogger << myAutosprintf(_("My wonderful %s ! I have %d apples"), name, nbApple); oLogger尼姆回答的轻微

我有一个日志类,它有
操作符一种方法是使用宏,即

#define LOG(x) \
{\
  <acquire scoped lock> \
  oLogger << x; \
}
#定义日志(x)\
{\
\

oLogger由于我必须将日志国际化,我更喜欢以下内容:

oLogger << myAutosprintf(_("My wonderful %s ! I have %d apples"), name, nbApple);

oLogger尼姆回答的轻微替代:

创造

class LockedLog {
    static MutEx mutex; // global mutex for logging
    ScopedLock lock; // some scoped locker to hold the mutex
    Logger &oLogger; // reference to the log writer itself
public:
    LockedLog(Logger &oLogger) : oLogger(oLogger), lock(mutex) {}
    template <typename T>
    LockedLog &operator<<(const T &value) { oLogger << value; return *this; }
};
这将为您当前的代码添加锁定


更新:锁定对
操作符的所有调用我发现最好的解决方案是编写一个类
缓冲区
,以便

buffer(oLogger) << "Log this" << " and this" << " and " << 10 << endl;

buffer(oLogger)我可能会在这里使用表达式模板

其主要思想是,在格式化阶段获取锁是非常愚蠢的,特别是在格式化期间可能会有函数调用

您需要使用两个不同的阶段:

  • 格式化日志
  • 原子地发布日志
这可以通过表达式模板实现:


  • 首先调用
    Logger::operator+1,原子性只能通过锁来确保,而且,如果没有锁,您甚至可能会导致意外的结果(可能是UB?),具体取决于记录器的实现(是否可重入?是否使用全局状态?
    LOG(“当前上下文:”@Matthieu同意,但是,将上述宏更改为流式处理为临时
    stringstream
    ,然后将结果写入
    oLogger
    ——重点是强调锁定很重要……我同意这很重要,但仍然很重要。至于锁定,我不知道是否有g面向多个客户的无锁队列的ood实现。我认为FF是通过组合多个SPSC无锁队列实现的。即使它是
    operator,也不认为您的第一个语句是正确的,第一次调用
    operator@david,尼姆:谢谢你的评论。我的观点主要是使用一个类来构建消息…我想知道
    ()
    是否能做到这一点…
    o触发器
    gnu::autosprintf
    看起来很有趣。但它不是类型安全的。boost.format()是。它比较慢,因为它通过
    stringstream
    ,但另一方面,它允许使用自定义
    操作符格式化对象。记录器是线程安全的吗?如果是,您可能只需要将字符串构建在一边(考虑以下情况:)并记录单个
    std::string
    ,但一般来说,遵循@Nim建议并锁定。对于Nim的回答,同样的批评是:您锁定的时间比需要的时间长。确实。使用临时缓冲区(stringstream)可以减少锁定。这将增加另一个开销,因此根据线程的数量,它可能会有回报,也可能不会有回报。
    operator@JohannesGerer:是的,它可以工作(stringstream的最后一个变体除外),因为临时表只在语句末尾被销毁。另一个选项是添加
    LockedLog&operator@JohannesGerer:我说过除了在带有stringstream的变体中,它确实可以工作,但在该变体中它也可以工作,只是您必须在那里返回stringstream。(哦,stringstream变体可能也应该刷新,因为您无法刷新那里的记录器;添加。)
    
    #define LOG(x) \
    {\
      std::ostringstream str; \
      str << x; \       // the streaming happens in local scope, no need for lock
      oLogger.write(str.str()); \ // ensure the write method acquires a lock
    }
    
    oLogger << myAutosprintf(_("My wonderful %s ! I have %d apples"), name, nbApple);
    
    class LockedLog {
        static MutEx mutex; // global mutex for logging
        ScopedLock lock; // some scoped locker to hold the mutex
        Logger &oLogger; // reference to the log writer itself
    public:
        LockedLog(Logger &oLogger) : oLogger(oLogger), lock(mutex) {}
        template <typename T>
        LockedLog &operator<<(const T &value) { oLogger << value; return *this; }
    };
    
    LockedLog(oLogger) << "Log this" << " and this " << " and " << 10 << endl;
    
    operator LockedLog() { return LockedLog(*this); }
    
    class LockedLog {
        static MutEx mutex; // global mutex for logging
        std::stringstream buffer; // temporary formatting buffer;
        Logger &oLogger; // reference to the log writer itself
    public:
        LockedLog(Logger &oLogger) : oLogger(oLogger), lock(mutex) {}
        template <typename T> 
        LockedLog &operator<<(const T &value) { buffer << value; return *this; }
        ~LockedLog() { ScopedLock lock(mutex); oLogger << buffer.str() << std::flush; }
    };
    
    buffer(oLogger) << "Log this" << " and this" << " and " << 10 << endl;