这是一个简单的演示代码片段。
有人告诉我复查锁不正确。由于变量是非 volatile 的,编译器可以自由地重新排序调用或优化它们(详情,请参阅 codereview.stackexchange.com/a/266302/226000)。
但我确实看到确实在许多项目中使用了这样的代码片段。有人可以对此事有所了解吗?我google了一下,和 friend 讨论过,还是没找到答案。
#include <iostream>
#include <mutex>
#include <fstream>
namespace DemoLogger
{
void InitFd()
{
if (!is_log_file_ready)
{
std::lock_guard<std::mutex> guard(log_mutex);
if (!is_log_file_ready)
{
log_stream.open("sdk.log", std::ofstream::out | std::ofstream::trunc);
is_log_file_ready = true;
}
}
}
extern static bool is_log_file_ready;
extern static std::mutex log_mutex;
extern static std::ofstream log_stream;
}
//cpp
namespace DemoLogger
{
bool is_log_file_ready{false};
std::mutex log_mutex;
std::ofstream log_stream;
}
更新:
感谢大家。 InitFd()
确实有更好的实现,但它确实只是一个简单的演示,我真正想知道的是仔细检查是否有任何潜在问题锁定与否。
完整的代码片段,见https://codereview.stackexchange.com/questions/266282/c-logger-by-template .
最佳答案
双重检查锁不正确,因为is_log_file_ready
是一个普通的 bool
,并且这个标志可以被多个线程访问,其中一个是写者——这是一场比赛
简单的解决方法是更改声明:
std::atomic<bool> is_log_file_ready{false};
然后您可以进一步放松对 is_log_file_ready
的操作:
void InitFd()
{
if (!is_log_file_ready.load(std::memory_order_acquire))
{
std::lock_guard<std::mutex> guard(log_mutex);
if (!is_log_file_ready.load(std::memory_order_relaxed))
{
log_stream.open("sdk.log", std::ofstream::out | std::ofstream::trunc);
is_log_file_ready.store(true, std::memory_order_release);
}
}
}
但一般来说,应避免双重检查锁定,除非是在低级实现中。
正如 Arthur P. Golubev 所建议的,C++ 提供了执行此操作的原语,例如 std::call_once
更新:
这里有一个例子说明了比赛可能导致的问题之一。
#include <thread>
#include <atomic>
using namespace std::literals::chrono_literals;
int main()
{
int flag {0}; // wrong !
std::thread t{[&] { while (!flag); }};
std::this_thread::sleep_for(20ms);
flag = 1;
t.join();
}
sleep
有没有给线程一些时间来初始化。
该程序应立即返回,但经过完全优化编译 -O3
,它可能不会。这是由有效的编译器转换引起的,它将 while 循环更改为以下内容:
if (flag) return; while(1);
如果 flag (仍然)为零,这将永远运行(将 flag
类型更改为 std::atomic<int>
将解决此问题)。
这只是未定义行为的影响之一,编译器甚至不必将更改提交到 flag
内存。
在比赛中,或错误设置(或丢失)障碍时,操作也可能被重新排序,从而导致不必要的影响,但这些不太可能发生在 X86
因为它通常是一个比较弱架构更宽容的平台(尽管重新排序会影响 X86
上的 do exist)
关于c++ - C++ 的双重检查锁是否有任何潜在问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68973582/