文章是对于《C++高级编程(第4版)》第570页中的《多线程的Logger类》代码解析。
具体代码在文章最后,读者可自行拷贝,研读代码。
从代码中可以看出,作者在析构函数中增加了mExit的赋值使得线程能够在类对象释放之前退出。
现在mMutex上获得一个锁,然后使用条件变量notify_all。
首先需要获得一个锁,即mMutex必须释放成功才能给mExit赋值为true。即在如下线程函数代码中,mQueue处理完成才会释放锁。就不会出现mQueue还未处理完成,马上就执行if(mExit)这一句,跳出线程。
然后notify_all是为了不是程序处于死锁的状态。假设没有notify_all,那线程很可能等在mCondVar.wait()这一句。而在析构函数中又执行到了mThread.join(),两边都在等待,程序和线程一直结束不了,应用程序就处在了死锁的状态。
代码摘自《C++高级编程(第4版)》第570页中的《多线程的Logger类》,作者Marc Gregoire。
Logger.h
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <string_view>
#include <mutex>
#include <queue>
#include <sstream>
using namespace std;
class Logger
{
public:
Logger();
virtual ~Logger();
Logger(const Logger& src) = delete;
Logger& operator=(const Logger& rhs) = delete;
void log(std::string entry);
private:
void processEntries();
std::mutex mMutex;
std::condition_variable mCondVar;
std::queue<std::string> mQueue;
std::thread mThread;
bool mExit = false;
};

Logger.cpp
#include "Logger.h"
Logger::Logger()
{
mThread = thread{&Logger::processEntries, this};
}
void Logger::log(std::string entry)
{
unique_lock<std::mutex> lock(mMutex);
mQueue.push(entry);
mCondVar.notify_all();
}
Logger::~Logger()
{
{
unique_lock<std::mutex> lock(mMutex);
mExit = true;
mCondVar.notify_all();
}
mThread.join();
}
void Logger::processEntries()
{
ofstream logFile("log.txt");
if (logFile.fail())
{
cerr << "Failed to open logfile." << endl;
return;
}
unique_lock<std::mutex> lock(mMutex);
while (true)
{
if(!mExit) mCondVar.wait(lock);
lock.unlock();
while (true)//循环处理队列直到为空
{
lock.lock();
if (mQueue.empty())
{
break;
}
else
{
logFile << mQueue.front() << endl;
mQueue.pop();
}
lock.unlock();
}
if (mExit)
{
cout << "mExit is true.\n";
break;
}
}
}

main.cpp
#include "Logger.h"
void logSomeMessage(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<thread> threads;
for (int i = 0; i < 10; ++i)
{
cout << "times: " << i << "\n";
threads.emplace_back(logSomeMessage, i , ref(logger));
}
for (auto& t : threads)
t.join();
cout << "hello world.\n";
return 0;
}
