2

元の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_;
    }
};
4

2 に答える 2

3

いいえ、これは安全ではありません。競合状態が発生しているためinstance_(1つのスレッドが読み取り中(if (instance_ == NULL))、別のスレッドが書き込み中(instance = temp;))、このコードの動作は未定義です。

ロックによって作成された単一のメモリフェンスで十分かどうかを尋ねています。C ++ 11の観点からは、そうではありません。非C++11の観点からは、はっきりとは言えませんが、同期のために非アトミックタイプと非ミューテックスタイプに依存することは機能しないようです(C ++ 11より前の世界では、ミューテックスとアトミック変数コンパイラとプロセッサ固有のハックを介してのみ機能し、それらの裸の仕様以上のことを行うためにそれらに依存するのは愚かなようです)。

于 2012-08-26T08:44:59.053 に答える
1

他の場所で述べたように、問題は へのアクセスにデータ競合があることですinstance_: 最初の if ステートメントが値を読み取り、その後の への代入instance_がその変数に書き込みます。これら 2 つの操作の間に同期がない場合、動作は未定義です。しかし、C++11 を使用している場合は、簡単な解決策があります。宣言を変更する

static T * instance_;

static std::atomic<T *> instance_;

現在、 のすべてのアクセスinstance_はアトミックであり、ティアリングがないことが保証され、同期が提供されます。

于 2012-08-26T15:31:40.443 に答える