1

I'm working on a multithread c++ application and actually I'm facing the problem of interleaved console output, caused by concurrent thread calls to cout/cerr. Note: I can't use boost/QT/other frameworks, but only standard c++.

As a temporary fix I'm using this class: (actually this is a win32 code snippet, that's why the use of CRITICAL_SECTION as temp workaround);

class SyncLogger
{
public:
    SyncLogger() { InitializeCriticalSection(&crit); }
    ~SyncLogger() { DeleteCriticalSection(&crit); }
    void print(std::ostringstream &os) {
        EnterCriticalSection(&crit);
        std::cout << os.str();
        os.str("");   // clean stream
        LeaveCriticalSection(&crit);
    }
private:
    CRITICAL_SECTION crit;
};

The usage is the following:

...
ostringstream ss;
ss << "Hello world, I'm a thread!" << endl;
syncLogger.print(ss);

I think this is very ugly, but it seems to work.

Btw thanks to this other question ( Best way to add a "prompt" message to std::cout) I have created the following logging class:

class MyLogger
{
    std::ostream & out;
    std::string const msg;
public:
    MyLogger(std::ostream & o, std::string s)
    : out(o)
    , msg(std::move(s))
    { }

    template <typename T>
    std::ostream & operator<<(T const & x)
    {
        return out << msg << x;
    }
};

So, does exist a way to provide built-in locking within the class MyLogger (using critical section or win32 mutex)? My best whish is that any thread will be able to print messages in a sync way, simply using

myLog << "thread foo log message" << endl;

and without the need to create an ostringstream object every time.

Thanks in advance.

Community
  • 1
  • 1
eang
  • 1,615
  • 7
  • 21
  • 41
  • 2
    Although there's definitely an approach like that, you should avoid taking it: console output is somewhat slow, and adding synchronization to achieve it would introduce random synchronization points between threads in your code. I worked with code that had a race condition that lead to a crash; that crash would never occur when the logging was on, because threads synchronized on the logger's methods! A better approach would be making a synchronized queue of log messages, writing to it from all your threads, and reading it from one thread that writes to `cout`/`cerr`. – Sergey Kalinichenko Aug 11 '13 at 09:33
  • 1
    @dasblinkenlight Do you mean a producer/consumer situation, right? N producer-threads write to the queue, one consumer-thread reads frome the queue. It seems a good solution – eang Aug 11 '13 at 09:55
  • 2
    @dasblinkenlight solution is indeed good. Given a slow and/or latency-ridden peripheral, (like a disk logger, DBL:), synchro on a queue push is much quicker than synchro on the peripheral, reducing lock time and hence contention. – Martin James Aug 11 '13 at 10:22
  • 1
    You are helping too much, most any practical implementation of cout already has such a lock built-in. You are happy with the way it now works not because of the critical section but because you are forced to use a single cout for output. Just remove the cs and it will still work. – Hans Passant Aug 11 '13 at 10:40
  • Also see [Is cout synchronized/thread-safe?](https://stackoverflow.com/q/6374264/608639) – jww May 27 '18 at 11:34

3 Answers3

2

I think the best way to go about this would be to have a message queue. You make sure only one thread can write to the queue at a time by using a mutex, and you have another thread reading from the queue and actually doing the output. This way, your worker threads wont have to wait for the console output to be written. This is especially important if multiple worker threads are trying to do output, since then they won't just have to wait on their own output to be done, but also for the output of the other threads, which could potentially seriously slow down your program.

JSQuareD
  • 4,641
  • 2
  • 18
  • 27
2

That as soon as you start doing asynchronous logging, you also risk losing the last few log entries if your app crashes. So you must catches SIGSEGV and other fatal signals (not SIGINT) and logs them before exiting. The message queue solution(The logger thread should be encapsuled within an active object) just transfers the contention on the lock/mutex outside to a separate background thread. It's possible to implement lock-free queues in C++ and that they are great for logging. But if you don't care about performance and scalability, only try to avoid output interleaving, you can do as Mats Petersson says. It's kind of like this:

class AsyncLogFile {
public:
  void write( string str )
    { a.Send( [=] { log.write( str ); } ); }
private:
  File log;    // private file
  ActiveObject a;    // private helper (queue+thread)
};
AsyncLogFile logFile;

// Each caller just uses the active object directly
string temp = …;
temp.append( … );
temp.append( … );
logFile.write( temp );
jfly
  • 7,715
  • 3
  • 35
  • 65
1

Adding a mutex to the logging mechanism shouldn't be that hard.

Assuming there is only ONE instance of MyLogger, then something like this should work:

class MyLogger
{
    std::ostream & out;
    std::string const msg;
    HANDLE mutex;
public:
    MyLogger(std::ostream & o, std::string s)
    : out(o)
    , msg(std::move(s))
    { 
       mutex = CreateMutex(0, FALSE, 0);
    }

    template <typename T>
    std::ostream & operator<<(T const & x)
    {
        WaitForSingleObject(mutex, INFINITE);
        out << msg << x;
        ReleaseMutex(mutex);
        return out;
    }
};

If there are multiple instances of MyLogger, then you need to make HANDLE mutex into static HANDLE mutex;, add a HANDLE MyLogger::mutex = 0; somewhere in a suitable .cpp file, and then use:

    MyLogger(std::ostream & o, std::string s)
    : out(o)
    , msg(std::move(s))
    { 
       if (!mutex) { 
           HANDLE tmp = CreateMutex(0, FALSE, "MyLoggerMutex");
           if (tmp) 
           {
              mutex = tmp;
           }
       }
    }

By using a name and a temporary variable, we avoid race conditions in creating more than one mutex (because there can only be one MyLoggerMutex in the system. [It gets more complicated if you also have multiple instances of your application running at the same time!]. And since I've assumed that there is only ever one instance of the application, I've also not taken into account that the mutex may be already existing... Or how to destroy the last instance... It will get destroyed when the application exits...

I do like dasblinkenlight's solution in the comment - it is a good solution.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Yes, there is only one instance. I have just tested this class, using Mutex it works fine. Just for curiosity I have replaced the mutex with a critical section, and I have no output overall; it really seems a deadlock (I have just two thread printing an hello message). – eang Aug 11 '13 at 10:23
  • Without seeing the code, it's hard to say what's wrong using a Critical Section variant. It should do the same thing, more or less... – Mats Petersson Aug 11 '13 at 10:33
  • It doesn't care, I have found a more serious problem: between the `ReleaseMutex()` e the `return` instruction, a waiting thread could break the sync. For example `myLogger << "log1" << endl;` and `myLogger << "log2" << endl;` could produce the output line ``. I'm not sure if the problem is `std::endl` or other things. – eang Aug 11 '13 at 11:20
  • 1
    Hmm. That's a good one. I don't think you can solve that inside the logger, because I believe that is caused by the compiler splitting the `myLogger << "Log1"` and `out << endl;`, where `out` is the `ostream` returned by `myLogger`. I don't know if you are able to read assembler enough to figure out if that is what is happening, but I expect that's what you will see... – Mats Petersson Aug 11 '13 at 11:28
  • So probably using a message queue is the safest way to go, I will try it. – eang Aug 11 '13 at 11:37
  • In c++20 one could use std::osyncstream(std::cout) << “my text from multiple threads” – David Constantine Apr 01 '22 at 18:16