我有一个operator<<
重载的日志记录类。所以我可以这样做:
oLogger << "Log this" << " and this" << " and " << 10 << endl;
oLogger`<< "Something else" << endl;
记录器没有任何问题。但是,我希望在线程之间共享logger对象。然后,我不希望它打印出这样的东西:
//LogFILE
Log this and this Something else
and 10
所以,我需要锁定整个operator<<
链。我猜这可以用RAII来完成,我还没有多想过。与此同时,有没有传统的方法来完成这项工作? (除了用操纵器结束输入?)
答案 0 :(得分:4)
Nim回答的轻微替代方案:
创建
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;
或者将Logger::operator<<
更改为常规方法,在LockedLog::operator<<
中调用此方法,将强制转换操作符添加到Logger
:
operator LockedLog() { return LockedLog(*this); }
并且应该为当前代码添加锁定。
更新:锁定对operator<<
和的所有调用甚至可以锁定其参数的评估(取决于编译器是评估左侧还是右侧)争论首先,它可能会选择)。为了减少这种情况,可以:
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; }
};
但是stringstream
增加了另一个开销。
答案 1 :(得分:3)
一种方法是使用宏,即
#define LOG(x) \
{\
<acquire scoped lock> \
oLogger << x; \
}
然后
LOG("Log this" << " and this" << " and " << 10 << endl);
我也是使用你上面提到的操纵器方法完成的,但是问题是你需要为所有类型实现operator<<
(即不能使用存在的标准运算符)< / p>
编辑:为了减少锁定的时间,请考虑以下事项:
#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
}
答案 2 :(得分:2)
我发现最好的解决方案是编写一个类buffer
以便
buffer(oLogger) << "Log this" << " and this" << " and " << 10 << endl;
创建一个临时缓冲区对象,捕获并格式化输出,并在其析构函数中将其写入oLogger。这可以通过包装stringstream
来完成。因为每个线程都有自己的缓冲区,格式化是独立的。
为了获得额外的好感,buffer::~buffer
可以使用几种不同的机制来防止oLogger
的线程不安全访问。您假设来自多个线程的operator<<
个调用可能是交错的。事实上,情况更糟;他们可以并发。你可以得到“LSoogm ethhiinsg else”。确保一次只有buffer
次oLogger
次冲到{{1}}就可以防止这种情况发生。
答案 3 :(得分:2)
我可能会在这里使用表达式模板。
主要的想法是在格式化阶段获取锁定是非常愚蠢的,特别是因为在格式化过程中可能会有函数调用。
您需要使用两个不同的阶段:
这可以通过表达式模板完成:
Logger::operator<<
会产生LoggerBuffer
,其中嵌入了对Logger
的引用。LoggerBuffer
执行,处理所有格式混乱LoggerBuffer
后(在语句末尾),它会锁定Logger
,传递格式化字符串并解锁(除非你有一个无锁队列或其他东西)< / LI>
醇>
答案 4 :(得分:1)
由于我必须将日志国际化,我更喜欢以下内容:
oLogger << myAutosprintf(_("My wonderful %s ! I have %d apples"), name, nbApple);
这对翻译来说更好:)它会解决你的问题。 _()
是翻译内容的快捷方式。
您可以使用gnu::autosprintf,boost.format(感谢Jan Huec),也可以自己编写。
MY2C
注意:在发表好评后编辑(太快了,谢谢你的评论)。我删除了错误的“第一部分”声明