2

原始双重检查锁定模式的问题已得到充分证明:C++ 和双重检查锁定的风险。我经常看到这个话题出现在关于 SO 的问题中。

我想出了一个版本,在我看来,它似乎解决了原始模式的竞争条件问题,但你觉得还可以吗?

在下面的代码中,我假设 LOCK 是正确实现的互斥锁类型,它会在锁定/解锁时导致内存屏障,并且我不会尝试处理实例的释放,因为这不是模式的一部分。

我知道做单例的其他可能方法,但这不是我要的。我特别询问模式 - 是否通过在互斥锁之外进行分配来解决竞争条件?

template <typename T, typename LOCK>
class Singleton
{
private:
    static T * instance_;
    static LOCK mutex;

    // private - inaccessible
    Singleton();
    Singleton(const Singleton &);
    Singleton & operator=(const Singleton &);

public:
    static T * get_instance()
    {
        if (instance_ == NULL)
        {
            T * temp = new T;
            mutex.lock();
            if (instance_ == NULL)
                instance = temp;
            else
                delete temp;
            mutex.unlock();
        }
        return instance_;
    }
};
4

2 回答 2

3

不,这不安全。存在竞争条件instance_(一个线程可能正在读取它(if (instance_ == NULL)),而另一个线程正在写入它(instance = temp;)),因此此代码具有未定义的行为。

您在询问由锁创建的单个内存围栏是否足够。从 C++11 的角度来看,它不是。从非 C++11 的角度来看,我不能肯定地说,但是依靠非原子和非互斥类型进行同步似乎不太可能工作(在 C++11 之前的世界中,互斥锁和原子变量只能通过编译器和处理器特定的 hack 来工作,依靠它们来做任何比它们的裸规范更多的事情似乎是愚蠢的)。

于 2012-08-26T08:44:59.053 回答
1

正如在别处提到的那样,问题在于对 的访问存在数据竞争instance_:第一个 if 语句读取该值,而后一个赋值语句instance_写入该变量。由于这两个操作之间没有同步,行为是未定义的。但是如果你有 C++11,那么有一个简单的解决方案。更改声明

static T * instance_;

static std::atomic<T *> instance_;

现在所有的访问instance_都是原子的,保证不撕裂并提供同步。

于 2012-08-26T15:31:40.443 回答