所以我见过很多文章声明在C++中双重检查锁定,通常用于防止多线程尝试初始化一个懒惰创建的单例,已被打破。普通双检查锁定代码读取这样的:这个修复程序对于双重检查锁定有什么问题?
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
public:
static singleton & instance()
{
static singleton* instance;
if(!instance)
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
}
return *instance;
}
};
的问题显然是行分配实例 - 编译器可以自由分配的对象,然后将指针分配给它,或者设定指针的地方将被分配,然后分配它。后一种情况会破坏成语 - 一个线程可能会分配内存并分配指针,但在其进入睡眠状态之前不会运行单例的构造函数 - 然后第二个线程将看到该实例不为null并尝试返回它,尽管它还没有建成。
我saw a suggestion使用线程本地布尔值,并检查,而不是instance
。事情是这样的:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
static boost::thread_specific_ptr<int> _sync_check;
public:
static singleton & instance()
{
static singleton* instance;
if(!_sync_check.get())
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
// Any non-null value would work, we're really just using it as a
// thread specific bool.
_sync_check = reinterpret_cast<int*>(1);
}
return *instance;
}
};
这样每个线程结束了,如果实例已经被创建一次检查,但在此之后停止,这需要一定的性能损失,但仍没有那么糟糕,因为每次调用锁定。但是,如果我们只是使用本地静态布尔呢?:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
public:
static singleton & instance()
{
static bool sync_check = false;
static singleton* instance;
if(!sync_check)
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
sync_check = true;
}
return *instance;
}
};
为什么不能这样工作?即使sync_check被另一个线程在另一个线程中分配时读取,垃圾值仍然不为零,因此也是如此。 This Dr. Dobb's article声称你必须锁定,因为你永远不会因为重新排序指令而与编译器争斗。这让我觉得这不应该出于某种原因,但我不明白为什么。如果序列点的要求像Dobb博士的文章让我相信的那样丢失,我不明白为什么锁之后的任何代码都不能重新排序为在锁之前。这将使C++多线程断开期间。
我想我可以看到编译器被允许特别重新排序sync_check在锁之前,因为它是一个局部变量(即使它是静态的,我们没有返回一个引用或指针) - 但是这样仍然可以通过使其成为静态成员(有效全局)来解决。
那么这项工作还是不会呢?为什么?
问题是变量可能在构造函数运行(或完成)之前分配,而不是在分配对象之前分配。 – kdgregory 2009-06-03 14:53:41
谢谢,纠正。我完全误解了比赛的状况。 – 2009-06-03 15:21:57
是的,你是对的,现在的C++确实是“多线程断点”。只考虑标准。编译器供应商通常会提供解决方法,因此实际结果并不那么糟糕。 – Suma 2009-06-16 15:58:27