7

これはhttp://www.ibm.com/developerworks/java/library/j-dcl/index.htmlからの Java の例の問題です。

public static Singleton getInstance()
{
  if (instance == null) //#4
  {
    synchronized(Singleton.class) {  //#1
      if (instance == null)          //#2
        instance = new Singleton();  //#3
    }
  }
  return instance;
}

コンストラクターが実行される前に #3 がインスタンスを非 null に設定できるため、これは安全ではないようです。したがって、別のスレッドが #4 でインスタンスをチェックすると、null ではなく、適切に構築されていないインスタンスが返されます。

関数変数を使用しても、最適化されていないか、インスタンスに値を設定したくないときに値を設定する方法で実行される可能性があるため、明らかに役に立ちません。

new Singleton();関数をインスタンスに割り当てる前に完了するのが最も簡単な方法ではないと考えていました。問題は、C++ に関数をインライン化しないように指示するにはどうすればよいかということです。私はそうすべきだと思います Singleton* make_singleton() volatileが、私は間違っていると確信しています。

4

2 に答える 2

26

しばらくの間、シングルトン ビットを無視します。これは、シングルトンのようなばかげたものではなく、遅延初期化に必要であると想定します。

二重チェックのロックを忘れることをお勧めします。C++ は、この種の状況に対して非常に便利なツールを の形式で提供しているstd::call_onceので、それを使用してください。

template <typename T>
struct lazy {
public:
    // needs constraining to prevent from doing copies
    // see: http://flamingdangerzone.com/cxx11/2012/06/05/is_related.html
    template <typename Fun>
    explicit lazy(Fun&& fun) : fun(std::forward<Fun>(fun)) {}

    T& get() const {
         std::call_once(flag, [this] { ptr.reset(fun()); });
         return *ptr;
    }
    // more stuff like op* and op->, implemented in terms of get()

private:
    std::once_flag flag;
    std::unique_ptr<T> ptr;
    std::function<T*()> fun;
};

// --- usage ---

lazy<foo> x([] { return new foo; });
于 2013-05-07T14:18:48.317 に答える
2

これはまさに、アトミックが設計されたタイプの状況です。結果をアトミックに格納することで、アトミックが設定された後、コンパイラーは重要なストアまたは操作をシーケンスできないことがわかります。アトミックは、プロセッサ命令プリミティブを発行して必要なシーケンシャルな一貫性を確保するため (たとえば、コア間のキャッシュの一貫性のため) と、どのセマンティクスを保持する必要があるかをコンパイラに伝えるため (したがって、コンパイラが実行できる並べ替えの種類を制限するため) の両方を目的としています。ここでアトミックを使用する場合、関数がインライン化されているかどうかは問題ではありません。これは、コンパイラが実行するインライン化は、アトミック自体のセマンティクスを保持する必要があるためです。

std::call_onceまた、この状況向けに設計されたもの、より具体的には、複数のスレッドで何かを行う必要があるが、そのうちの 1 つだけがそれを行う必要がある状況向けに設計されているものを調べることにも興味があるかもしれません。

于 2013-05-07T14:11:39.963 に答える