异步记录器线程安全

时间:2018-02-11 15:42:42

标签: c++ thread-safety

我写了一个简单的记录器,我有一个问题。

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

提前致谢。那里有银弹还是需要新方法?

由于

1 个答案:

答案 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个对象获取一次的互斥锁,并且直到所有菊花链式<<运算符才会释放完成了他们的责任。

第一个<<运算符由上面的原始<<运算符启动,并且它们都由辅助对象的<<运算符重载完成处理,移动语义负责保留底层互斥锁,并在最后释放它。