简体   繁体   中英

async logger thread safe

I have a simple logger written, I have a problem.

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;

When I use the logger however from two threads, it does exactly what it shouldn't do. The calls to;

Thread #1

log << "Hello " << 1;

Thread #2

log << "Goodbye " << 2;

The output gets interleaved because of the way operator<< is implemented. How would I be able to get natural use of << AND have it be atomic in nature so instead of

Hello 2 Goodbye 1

We are guaranteed to get

Hello 1 Goodbye 2

Thanks in advance. Is there a silver bullet there or is a new approach required?

Thanks

G

Use a helper object.

Change your existing << overload method to return a helper object, something like:

  template<typename T>
  loggerHelper operator<<(const T& t)
  {
     return loggerHelper{} << t;
  }

Your loggerHelper class must have the following properties:

  • its constructor should acquire the mutex. And the destructor will unlock the mutex. Run-of-the-mill RAII design pattern, but with one additional twist:

  • the object must correctly, and precisely, implement move semantics. The moved-from object instance will not release the aforementioned mutex, the moved-to object will be responsible for that. The copy constructor and the assignment operator must be delete d.

  • the object implements the "real" << operator overload, using std::cout and returns *this . This will trigger move semantics, and passing the "hot potato" of releasing the mutex to the next << operator invocation, or if there is none, finally destroying the end-of-the-road helper class instance.

If all these steps are done correctly, then I would expect the end result will be the mutex on the main logger object getting acquired once, and not released until all the daisy-chained << operators are finished with their responsibilities.

The first << operator gets kicked off by the original << operator, above, and they all get processed, for real, by the helper object's << operator overload, with move semantics taking care of preserving the underlying mutex lock, and releasing it at the end.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM