In order to learn about multithreading programming in C++, I am implementing a basic multithreaded logger.
I use a std::deque
to store messages inside a FileLogger
class. Each time a thread logs a message, that message is pushed to the back of the deque.
In a separate thread the FileLogger
checks if there are any messages in the deque and if so writes them to file.
Access to the deque is guarded by a mutex.
In order to make it easy to log from anywhere, the logger is implemented as a singleton.
Is my code correct? How can it be improved?
// FileLogger.h:
class FileLogger
{
public:
static void initialize(const char* filePath) { // called by main thread before any threads are spawned
instance_ = new FileLogger(filePath);
}
static FileLogger* instance() { // called from many threads simultaneously
return instance_;
}
void log(const std::string &msg);
private:
FileLogger(const char* filePath);
void writeToFile();
static FileLogger* instance_;
std::deque<std::string> messages;
std::mutex messagesMutex; // lock/unlock this each time messages is pushed or popped
std::ofstream fout;
std::thread writerThread;
};
// FileLogger.cpp:
FileLogger* FileLogger::instance_ = nullptr;
void FileLogger::writeToFile() {
for (;;) {
std::string message;
while (messages.empty()) {
std::this_thread::sleep_for(std::chrono::nanoseconds(10));
}
messagesMutex.lock();
message = messages.front();
messages.pop_front();
messagesMutex.unlock();
fout << message << std::endl << std::flush;
}
}
FileLogger::FileLogger(const char* filePath) {
fout.open(filePath);
std::thread t(&FileLogger::writeToFile, this);
writerThread = std::move(t);
}
void FileLogger::log(const std::string &msg) {
std::lock_guard<std::mutex> lg(messagesMutex);
messages.push_back(msg);
}