这是否可以兑换线程安全的双重锁定模式?

时间:2012-08-26 08:32:51

标签: c++ multithreading singleton double-checked-locking

原始Double-Checked Locking模式的问题已有详细记录:C++ and the Perils of Double-Checked Locking。我已经经常看到关于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_;
    }
};

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

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

static T * instance_;

static std::atomic<T *> instance_;

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