我在一个类中有一个私有的静态方法,这个类的所有方法都是静态的。它是一个帮助类,帮助记录日志和其他东西。这个帮助类会被多个线程调用。我不明白这个方法是如何在没有锁的情况下安全地与多个线程一起工作的。
//Helper.cpp
std::recursive_mutex Logger::logMutex_;
void Logger::write(const std::string &logFilePath, const std::string &formattedLog)
{
std::lock_guard<std::mutex> guard(logMutex_);
std::ofstream logFile(logFilePath.c_str(), std::ios::out | std::ios::app);
if (logFile.is_open())
{
logFile << formattedLog;
logFile.close();
}
}
void Logger::error(const string &appId, const std::string &fmt, ...)
{
auto logFile = validateLogFile(appId); //Also a private static method that validates if a file exists for this app ID.
if (!logFile.empty())
{
//Format the log
write(logFile, log);
}
}
//Helper.h
class Logger
{
public:
static void error(const std::string &Id, const std::string &fmt, ...);
private:
static void write(const std::string &fileName, const std::string &formattedLog);
static std::recursive_mutex logMutex_;
};
我知道静态方法中的局部变量是纯局部的。也就是说,每次调用这些方法时都会创建一个堆栈,并在堆栈中初始化变量。现在,在Logger::write方法中,我打开一个文件并向其写入。因此,当多个线程通过Logger::error方法(也是静态的)调用write方法时,并且当没有锁时,我相信我应该会看到一些数据竞争/崩溃。
因为多个线程试图打开同一个文件。即使内核允许多次打开一个文件,我也必须在写入文件的数据中看到一些错误。
我在运行多达100个线程的情况下进行了测试,没有看到崩溃,所有数据都在并发地写入文件。我不能完全理解这是如何工作的。无论有没有锁,我看到数据都在完美地写入文件。
TEST_F(GivenALogger, WhenLoggerMethodsAreCalledFromMultipleThreads_AllTheLogsMustBeLogged)
{
std::vector<std::thread> threads;
int num_threads = 100;
int i = 0;
for (; i < num_threads / 2; i++)
{
threads.push_back(std::thread(&Logger::error, validId, "Trial %d", i));
}
for (; i < num_threads; i++)
{
threads.push_back(std::thread(&Logger::debug, validId, "Trial %d", i));
}
std::for_each(threads.begin(), threads.end(), [](std::thread &t) { t.join(); });
auto actualLog = getActualLog(); // Returns a vector of log lines.
EXPECT_EQ(num_threads, actualLog.size());
}
另外,我应该如何正确/安全地访问该文件?
1条答案
按热度按时间v9tzhpje1#
关键是这一行:
std::lock_guard<std::mutex>
将锁定构造函数中的互斥体logMutex_
,并在析构函数中的互斥体超出作用域时解锁,即当方法返回时。如果另一个线程试图在第一个线程处于保护范围内时进行写入,则新的(本地)保护将尝试锁定
logMutex_
,并且该线程将被置于睡眠状态,直到锁定被释放。