33

インスタンス変数が GetInstance メソッドで静的変数として宣言されたシングルトン パターンの実装を見てきました。このような:

SomeBaseClass &SomeClass::GetInstance()
{
   static SomeClass instance;
   return instance;
}

このアプローチには、次のような良い面があります。

  • GetInstance が初めて呼び出されたときにのみ、このオブジェクトの作成を担当するのはコンパイラであるため、コードはより単純です。
  • インスタンスへの参照を取得する方法は他にありませんが、GetInstance メソッドを使用し、インスタンスを変更する方法は他にありませんが、GetInstance メソッド内であるため、コードはより安全です。

このアプローチのマイナス面は何ですか? これはスレッドセーフですか?

4

4 に答える 4

44

C++11 では、スレッド セーフです。

§6.7 [stmt.dcl] p4 変数の初期化中に制御が同時に宣言に入った場合、同時実行は初期化の完了を待つ必要があります。

C++03 の場合:

  • g++ ではスレッドセーフです。
    しかし、これは g++ がそれを保証するコードを明示的に追加するためです。

1 つの問題は、2 つのシングルトンがあり、構築と破棄の際にお互いを使用しようとすることです。

これを読んでください: C++の静的初期化順序の問題を見つける

この問題のバリエーションとして、シングルトンがグローバル変数のデストラクタからアクセスされる場合があります。この状況では、シングルトンは確実に破棄されていますが、get メソッドは破棄されたオブジェクトへの参照を返します。

これを回避する方法はありますが、それらは面倒であり、実行する価値はありません。グローバル変数のデストラクタからシングルトンにアクセスしないでください。

より安全な定義ですが、醜い:
これを整理するために適切なマクロを追加できると確信しています

SomeBaseClass &SomeClass::GetInstance()
{
#ifdef _WIN32 
Start Critical Section Here
#elif  defined(__GNUC__) && (__GNUC__ > 3)
// You are OK
#else
#error Add Critical Section for your platform
#endif

    static SomeClass instance;

#ifdef _WIN32
END Critical Section Here
#endif 

    return instance;
}
于 2009-01-16T08:20:33.037 に答える
5

示されているように、スレッドセーフではありません。C++ 言語はスレッドに対してサイレントであるため、言語から固有の保証はありません。アクセスを保護するには、Win32 ::EnterCriticalSection() などのプラットフォーム同期プリミティブを使用する必要があります。

あなたの特定のアプローチは、コンパイラがinstance最初の呼び出しで静的を初期化するためにいくつかの(スレッドセーフではない)コードを挿入するため、問題になるでしょう。おそらく、関数本体が実行を開始する前(したがって、同期を呼び出す前)になります。 )

グローバル/静的メンバー ポインターを使用しSomeClassて同期ブロック内で初期化すると、実装の問題が少なくなります。

#include <boost/shared_ptr.hpp>

namespace
{
  //Could be implemented as private member of SomeClass instead..
  boost::shared_ptr<SomeClass> g_instance;
}

SomeBaseClass &SomeClass::GetInstance()
{
   //Synchronize me e.g. ::EnterCriticalSection()
   if(g_instance == NULL)
     g_instance = boost::shared_ptr<SomeClass>(new SomeClass());
   //Unsynchronize me e.g. :::LeaveCriticalSection();
   return *g_instance;
}

私はこれをコンパイルしていないので、説明のみを目的としています。また、ブースト ライブラリに依存して、元の例と同じ寿命 (またはその程度) を取得します。std::tr1 (C++0x) も使用できます。

于 2009-01-16T06:32:39.213 に答える
1

仕様によると、これは VC++ でも動作するはずです。あるかどうか知っている人はいますか?

キーワード volatile を追加するだけです。msdn のドキュメントが正しい場合、ビジュアル C++ コンパイラはミューテックスを生成する必要があります。

SomeBaseClass &SomeClass::GetInstance()
{
   static volatile SomeClass instance;
   return instance;
}
于 2010-08-18T15:25:31.520 に答える
-11

シングルトン実装の一般的な失敗をすべて共有します。つまり、次のとおりです。

  • それはテスト不可能です
  • これはスレッド セーフではありません (これは、2 つのスレッドが同時に関数に入るのを想像するだけで十分です)。
  • メモリリークです

本番コードでは Singleton を使用しないことをお勧めします。

于 2009-01-16T04:07:43.720 に答える