I'm new to C++ and am following up on: A simple multithreaded FileLogger in C++
I am curious to know if I implemented his ideas correctly, if there are further improvements to the code and whether it's (still) correct.
FileLoggerThread.h
#pragma once
#include <string>
#include <fstream>
#include <mutex>
#include <deque>
#include <condition_variable>
#include <filesystem>
class FileLoggerThread
{
public:
explicit FileLoggerThread(std::filesystem::path filePath);
~FileLoggerThread(); // makes code more easily unit testable (unit tests can otherwise be in an infinite loop)
void log(const std::string &msg);
FileLoggerThread(const FileLoggerThread&) = delete;
FileLoggerThread& operator=(const FileLoggerThread&) = delete;
private:
void writeToFile();
std::deque<std::string> mMessages; // accessed by log (any thread) and writeToFile (the writer thread)
std::ofstream mOutputFile; // only accessible by writeToFile
// thread internal resources
std::mutex mMessagesMutex; // lock/unlock this each time messages is pushed or popped
std::condition_variable mThreadScheduler; // log needs to wake up writeToFile
bool mIsRunning; // if set to false then the writer thread stops
};
FileLoggerThread.cpp
#include "FileLoggerThread.h"
#include <thread>
FileLoggerThread::FileLoggerThread(std::filesystem::path filePath) : mOutputFile {filePath}, mIsRunning {true} {
std::thread t(&FileLoggerThread::writeToFile, this);
t.detach();
}
void FileLoggerThread::writeToFile() {
while (mIsRunning) {
std::deque<std::string> localMessages;
std::unique_lock<std::mutex> writerLock {mMessagesMutex};
mThreadScheduler.wait(writerLock, [&]{ return !mMessages.empty() || !mIsRunning;});
localMessages.swap(mMessages);
writerLock.unlock();
for (const auto& message : localMessages) {
mOutputFile << message << std::endl;
}
}
}
void FileLoggerThread::log(const std::string &msg) {
std::unique_lock loggerLock(mMessagesMutex);
mMessages.emplace_back(msg);
loggerLock.unlock();
mThreadScheduler.notify_one(); // done for performance reasons, see: https://youtu.be/F6Ipn7gCOsY?t=2205
}
FileLoggerThread::~FileLoggerThread() {
mIsRunning = false;
mThreadScheduler.notify_one();
}