我写了一个简单的记录器,我有一个问题。
pthread_mutex_t l_mutex = PTHREAD_MUTEX_INITIALIZER;
class logger
{
public:
logger() { }
template<typename T>
logger& operator<<(const T& t)
{
pthread_mutex_lock(&l_mutex);
std::cout << t << std::flush;
pthread_mutex_unlock(&l_mutex);
return *this;
}
protected:
};
static const unsigned char endline = '\n';
static logger log;
当我从两个线程使用记录器时,它确实完成了它不应该做的事情。致电;
线程#1
log << "Hello " << 1;
线程#2
log << "Goodbye " << 2;
由于运算符&lt;&lt;&lt;&lt;&lt;&lt;实施。我怎样才能自然地使用&lt;&lt;并且它本质上是原子的,而不是
你好2再见1
我们保证得到
你好1 再见2
提前致谢。那里有银弹还是需要新方法?
由于
答案 0 :(得分:1)
使用辅助对象。
更改现有的<<
重载方法以返回辅助对象,如:
template<typename T>
loggerHelper operator<<(const T& t)
{
return loggerHelper{} << t;
}
您的loggerHelper
课程必须具有以下属性:
其构造函数应获取互斥锁。析构函数将解锁互斥锁。普通的RAII设计模式,但还有一个额外的转折:
对象必须正确,并且精确地实现移动语义。移动的对象实例不会释放上述互斥锁,移动对象将对此负责。复制构造函数和赋值运算符必须为delete
d。
该对象实现了&#34; real&#34; <<
运算符重载,使用std::cout
并返回*this
。这将触发移动语义,并传递&#34;烫手山芋&#34;将互斥锁释放到下一个<<
运算符调用,或者如果没有,最后会破坏末尾的辅助类实例。
如果所有这些步骤都正确完成,那么我希望最终结果将是主logger
个对象获取一次的互斥锁,并且直到所有菊花链式<<
运算符才会释放完成了他们的责任。
第一个<<
运算符由上面的原始<<
运算符启动,并且它们都由辅助对象的<<
运算符重载完成处理,移动语义负责保留底层互斥锁,并在最后释放它。