From what I read, the standard output streams are generally not thread safe. I have a C++ application (Windows-based, using Visual Studio 2005) that has a very simple logging function:
void logText(string text)
{
if(g_OutputLogEnabled && g_OutputLog.is_open())
{
string logDate = getDateStamp("%Y-%m-%d %H:%M:%S");
g_OutputLog << "[" << logDate << "]: " << text << endl;
}
cout << text << endl; // Also echo on stdout
}
In this example, g_OutputLog is an ofstream and g_OutputLogEnabled is a boolean.
I've been using this small function in my main application with no problem, but I now want to extend its use to some child threads. These threads do work and asynchronously print data as that work is done.
Question: How can I add simple, line-level thread-safety to this routine? All that I really care about is that each line that comes in remains intact in my log. Performance isn't a concern in this case, but (as always) faster is nicer.
I'm aware I could use third party logging packages, but I want to do it myself so I can learn how it works. My multi-threading knowledge isn't what it should be, and I'm trying to improve that.
I've heard the term critical sections, and I'm somewhat aware of mutexes and semaphores, but which would I use in this case? Is there a clean, simple solution? Thanks in advance for any advice.
Use scoped lock such as:
void logText(string text)
{
if(g_OutputLogEnabled && g_OutputLog.is_open())
{
string logDate = getDateStamp("%Y-%m-%d %H:%M:%S");
boost::scoped_lock (g_log_mutex); //lock
g_OutputLog << "[" << logDate << "]: " << text << endl;
} //mutex is released automatically here
boost::scoped_lock (g_cout_log_mutex); //lock on different mutex!
cout << text << endl; // Also echo on stdout
}
Or you could use std::unique_lock if your compiler supports this.
scoped_lock if you cannot use Boost and if you don't have std::unique_lock?First define mutex class:
#include <Windows.h>
class mutex : private CRITICAL_SECTION //inherit privately!
{
public:
mutex()
{
::InitializeCriticalSection(this);
}
~mutex()
{
::DeleteCriticalSection(this);
}
private:
friend class scoped_lock; //make scoped_lock a friend of mutex
//disable copy-semantic
mutex(mutex const &); //do not define it!
void operator=(mutex const &); //do not define it!
void lock()
{
::EnterCriticalSection(this);
}
void unlock()
{
::LeaveCriticalSection(this);
}
};
Then define scoped_lock as:
class scoped_lock
{
mutex & m_mutex;
public:
scoped_lock(mutex & m) : m_mutex(m)
{
m_mutex.lock();
}
~scoped_lock()
{
m_mutex.unlock();
}
};
Now you can use them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With