Online Book Reader

Home Category

Professional C__ - Marc Gregoire [399]

By Root 1563 0
acquires and releases the lock on each iteration. This is done to make sure the loop doesn’t keep the lock for too long, blocking other threads.

Logger::Logger()

{

// Start background thread.

mThread = thread{&Logger::processEntries, this};

}

void Logger::log(const std::string& entry)

{

// Lock mutex and add entry to the queue.

unique_lock lock(mMutex);

mQueue.push(entry);

// Notify condition variable to wake up thread.

mCondVar.notify_all();

}

void Logger::processEntries()

{

// Open log file.

ofstream ofs("log.txt");

if (ofs.fail()) {

cerr << "Failed to open logfile." << endl;

return;

}

// Start processing loop.

unique_lock lock(mMutex);

while (true) {

// Wait for a notification.

mCondVar.wait(lock);

// Condition variable is notified, so something is in the queue.

lock.unlock();

while (true) {

lock.lock();

if (mQueue.empty()) {

break;

} else {

ofs << mQueue.front() << endl;

mQueue.pop();

}

lock.unlock();

}

}

}

Code snippet from logger\Version1\Logger.cpp

This Logger class can be tested by using the following test code, which launches a number of background threads, all logging a few messages to the same Logger instance:

void logSomeMessages(int id, Logger& logger)

{

for (int i = 0; i < 10; ++i) {

stringstream ss;

ss << "Log entry " << i << " from thread " << id;

logger.log(ss.str());

}

}

int main()

{

Logger logger;

vector threads;

// Create a few threads all working with the same Logger instance.

for (int i = 0; i < 10; ++i) {

threads.push_back(thread{logSomeMessages, i, ref(logger)});

}

// Wait for all threads to finish.

for (auto& t : threads) {

t.join();

}

return 0;

}

Code snippet from logger\Version1\main.cpp

If you build and run this naïve initial version, you will notice a couple of problems with it, especially when you run it on a multicore machine.

The first problem is that the background Logger thread will be terminated abruptly when the main() function finishes. This means that messages still in the queue will not be written to the file on disk. Some run-time libraries will even issue an error or generate a crash dump when the background Logger thread is abruptly terminated. You need to add a mechanism to gracefully shut down the background thread and wait until the background thread is completely shut down before terminating the application itself. This can be done by adding a Boolean member variable to the class. The destructor of the Logger class will set this Boolean to true, notify the background thread, and wait until the thread is shut down. The notification will trigger the background thread to wake up, check this Boolean value and, if it’s true, write all messages in the queue to the file and terminate the processing loop. The new definition of the class is as follows:

class Logger

{

public:

// Starts a background thread writing log entries to a file.

Logger();

// Gracefully shut down background thread.

virtual ~Logger();

// Add log entry to the queue.

void log(const std::string& entry);

protected:

void processEntries();

bool mExit;

// Remaining of the class omitted for brevity

};

Code snippet from logger\Version2\Logger.h

The Logger constructor needs to initialize the mExit variable:

Logger::Logger() : mExit(false)

{

// Start background thread.

mThread = thread{&Logger::processEntries, this};

}

Code snippet from logger\Version2\Logger.cpp

The destructor sets the Boolean variable, wakes up the thread, and then waits until the thread is shut down:

Logger::~Logger()

{

// Gracefully shut down the thread by setting mExit

// to true and notifying the thread.

mExit = true;

// Notify condition variable to wake up thread.

mCondVar.notify_all();

// Wait until thread is shut down.

mThread.join();

}

Code snippet from logger\Version2\Logger.cpp

The processEntries() method needs to check this Boolean variable and terminate the processing loop when it’s true:

void Logger::processEntries()

{

// Open log file.

ofstream ofs("log.txt");

if (ofs.fail()) {

cerr << "Failed to open logfile."

Return Main Page Previous Page Next Page

®Online Book Reader