gpt4 book ai didi

multithreading - 双重检查锁定的此修复程序有什么问题?

转载 作者:行者123 更新时间:2023-12-04 06:39:50 24 4
gpt4 key购买 nike

因此,我看过很多文章声称在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使用线程本地 bool 值,并检查它而不是 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;
}
};

这样,每个线程最终都会检查实例是否已创建一次,但是此后停止,这虽然会降低性能,但仍不如锁定每个调用那么糟糕。但是,如果我们仅使用局部静态 bool 怎么办?:
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在一个线程中分配给另一个线程时,该值仍为非零,因此为true。 This Dr. Dobb's article声称您必须锁定,因为您永远不会在重新排序指令方面赢得与编译器的战斗。这使我认为这出于某些原因不能奏效,但我不知道为什么。如果对序列点的要求像Dobb博士的文章一样使我信服,那么我不明白为什么不能将锁后的任何代码重新排序为锁前的代码。这会使C++多线程中断。

我想我可以看到编译器被允许重新指定sync_check在锁之前,因为它是一个局部变量(即使它是静态的,我们也没有返回它的引用或指针),但是仍然可以解决通过使其成为静态成员(实际上是全局成员)。

那么,这项工作还是行不通?为什么?

最佳答案

您的修复无法解决任何问题,因为对sync_check和实例的写入可以在CPU上无序进行。例如,假设对实例的前两次调用大约在两个不同的CPU上同时发生。第一个线程将以该顺序获取锁,初始化指针并将sync_check设置为true,但是处理器可以更改写入内存的顺序。在另一个CPU上,第二个线程可以检查sync_check,看是否为true,但是实例可能尚未写入内存。有关详细信息,请参见Lockless Programming Considerations for Xbox 360 and Microsoft Windows

您提到的特定于线程的sync_check解决方案应该可以正常工作(假设您将指针初始化为0)。

关于multithreading - 双重检查锁定的此修复程序有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/945232/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com