简介
我正在使用多线程跨平台日志记录工具,该工具旨在记录到控制台和/或文件中。但是,我现在遇到的问题仅与控制台日志记录有关。
问题细分
记录器的工作方式是将字符串添加到字符串队列中。在“ LogToConsole”线程中,有一个wait方法,该方法等待直到队列中添加了字符串。发生这种情况时,它应该得到通知并打印,然后弹出并解锁。
使用的变量
class Logger
{
public:
friend void LogToConsole(Logger* logger);
private:
std::atomic_flag _ThreadIsRunning { ATOMIC_FLAG_INIT };
std::thread _LogThread;
std::queue<std::string> _LogBuffer;
std::map<std::thread::id, std::string> _ThreadName;
std::mutex _LogMutex;
std::mutex _AppendLock;
std::condition_variable _LogLock;
...
我将数据添加到缓冲区的位置
template<LogSeverity severity>
void Logger::Log(std::stringstream& message)
{
std::stringstream log;
log << _ThreadName[std::this_thread::get_id()] << ":\t";
log << message.str();
std::unique_lock<std::mutex> logAppendLock(_AppendLock);
_LogBuffer.push(log.str());
logAppendLock.unlock();
_LogLock.notify_one();
}
template<LogSeverity severity>
void Logger::Log(std::string message)
{
std::stringstream log;
log << message.c_str();
this->Log<severity>(log);
}
在单独的循环中运行的线程(但是请注意,该方法不属于logger类的一部分):
void LogToConsole(Logger* logger)
{
do
{
std::unique_lock<std::mutex> lock(logger->_LogMutex);
logger->_LogLock.wait(lock);
std::printf("%s", logger->_LogBuffer.back().c_str());
logger->_LogBuffer.pop();
lock.unlock();
} while (logger->_ThreadIsRunning.test_and_set() || !logger->_LogBuffer.empty());
}
线程创建的地方
Logger::Logger() : _LogThread(), _LogBuffer(), _ThreadName(), _LogMutex()
{
_ThreadIsRunning.test_and_set();
_LogThread = std::thread(LogToConsole, this);
}
测试框
std::shared_ptr<Logger> engineLogger = std::make_shared<Logger>();
engineLogger->SetThreadName("EngineLogger");
std::shared_ptr<Logger> coreLogger = std::make_shared<Logger>();
coreLogger->SetThreadName("CoreLogger");
while(true)
{
engineLogger->Log<LOG_INFO>("LOG\n");
coreLogger->Log<LOG_WARNING>("WARNING\n");
}
该代码似乎是线程安全的,没有数据争用等,但是它会在5-10秒后崩溃。我已经搜索过是否有人遇到类似的问题,但事实并非如此。
但是我对并发编程不是很有经验,因此现在不要很轻松地处理这类问题。
希望有人可以解决问题或给我一些建议以防止出现问题。
答案 0 :(得分:0)
在while (logger->_ThreadIsRunning.test_and_set() || !logger->_LogBuffer.empty());
中,您无需互斥就访问logger
,所以这是一场数据竞赛。另外,_LogBuffer.push(log.str());
只能在_AppendLock
锁定的情况下访问,而logger->_LogBuffer.pop()
只能在_LogMutex
锁定的情况下访问,所以这是另一种数据竞争。
数据争用是UB,也是崩溃的可能原因。