71

C++ 11 がマルチスレッド化されたので、(パフォーマンス上の理由から) ミューテックスを使用せずに遅延初期化シングルトンを実装する正しい方法は何か疑問に思っていました。私はこれを思いつきましたが、私はロックフリーのコードを書くのがあまり得意ではないので、より良い解決策を探しています。

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
# include <atomic>
# include <thread>
# include <string>
# include <iostream>
using namespace std;
class Singleton
{

public:
    Singleton()
    {
    }
static  bool isInitialized()
    {
        return (flag==2);
    }
static  bool initizalize(const string& name_)
    {
        if (flag==2)
            return false;// already initialized
        if (flag==1)
            return false;//somebody else is initializing
        if (flag==0)
        {
            int exp=0;
            int desr=1;
            //bool atomic_compare_exchange_strong(std::atomic<T>* obj, T* exp, T desr)
            bool willInitialize=std::atomic_compare_exchange_strong(&flag, &exp, desr);
            if (! willInitialize)
            {
                //some other thread CASed before us
                std::cout<<"somebody else CASed at aprox same time"<< endl;
                return false;
            }
            else 
            {
                initialize_impl(name_);
                assert(flag==1);
                flag=2;
                return true;
            }
        }
    }
static void clear()
{
    name.clear();
    flag=0;
}
private:
static  void initialize_impl(const string& name_)
{
        name=name_;
}
static  atomic<int> flag;
static  string name;
};
atomic<int> Singleton::flag=0;
string Singleton::name;
void myThreadFunction()
{
    Singleton s;
    bool initializedByMe =s.initizalize("1701");
    if (initializedByMe)
        s.clear();

}
int main()
{
    while (true)
    {
        std::thread t1(myThreadFunction);
        std::thread t2(myThreadFunction);
        t1.join();
        t2.join();
    }
    return 0;
}

clear()テスト用であり、実際のシングルトンにはその機能がないことに注意してください。

4

6 に答える 6

162

C ++ 11を使用すると、手動でロックする必要がなくなります。静的ローカル変数がすでに初期化されている場合、同時実行は待機します。

§6.7 [stmt.dcl] p4

変数の初期化中に制御が宣言に同時に入る場合、同時実行は初期化の完了を待機する必要があります。

そのため、simpleには次のstaticような関数があります。

static Singleton& get() {
  static Singleton instance;
  return instance;
}

これはC++11で問題なく機能します(もちろん、コンパイラーが標準のその部分を適切に実装している限り)。


もちろん、本当の正解は、シングルトンのピリオドを使用しないことです。

于 2012-07-29T18:52:21.700 に答える
46

おそらく、C++11 を使用してシングルトンを実装する最も簡単な方法は次のとおりです。

警告: これは C++11 標準 (静的初期化子はスレッドセーフ) に従って機能しますが、Microsoft Visual C++ 2012 では正しく実装されていません (静的初期化子はスレッドセーフではありません)。VC2012 をターゲットにしている場合は、C++11 標準を完全に実装していないため、別のアプローチを使用する必要があります。

class Singleton {
 public:
  static Singleton& Instance() {
    // Since it's a static variable, if the class has already been created,
    // it won't be created again.
    // And it **is** thread-safe in C++11.
    static Singleton myInstance;

    // Return a reference to our instance.
    return myInstance;
  }

  // delete copy and move constructors and assign operators
  Singleton(Singleton const&) = delete;             // Copy construct
  Singleton(Singleton&&) = delete;                  // Move construct
  Singleton& operator=(Singleton const&) = delete;  // Copy assign
  Singleton& operator=(Singleton &&) = delete;      // Move assign

  // Any other public methods.

 protected:
  Singleton() {
    // Constructor code goes here.
  }

  ~Singleton() {
    // Destructor code goes here.
  }

 // And any other protected methods.
}
于 2016-01-07T11:03:09.517 に答える
2

コードを意図したとおりに使用していないため、アプローチを読むのは難しいです...つまり、シングルトンの一般的なパターンはinstance()、単一のインスタンスを取得するために呼び出してから、それを使用します(また、本当にシングルトンが必要な場合は、いいえコンストラクターはパブリックにする必要があります)。

とにかく、あなたのアプローチが安全だとは思いません.2つのスレッドがシングルトンを取得しようとすると、フラグを更新する最初のスレッドだけが初期化されますが、initialize関数は2番目のスレッドで早期に終了します最初のスレッドが初期化を完了する前に、そのスレッドがシングルトンの使用に進む可能性があります。

あなたのセマンティクスinitializeは壊れています。関数の動作を説明/文書化しようとすると、楽しいことがあり、単純な操作ではなく実装を説明することになります。文書化は、通常、デザイン/アルゴリズムを再確認する簡単な方法です。を説明するの ではなく、どのように説明することになった場合は、設計に戻る必要があります。特に、完了後にオブジェクトが実際に初期化されているという保証はありません (戻り値がである場合のみ、場合によっては である場合もありますが、常にではありません)。initializetruefalse

于 2012-07-29T20:07:13.727 に答える