13

メソッド内で宣言された静的変数はスレッドセーフではないことを読んだことを覚えています。(Todd Gardnerが述べたように、Meyerのシングルトンはどうですか?を参照してください)

Dog* MyClass::BadMethod()
{
  static Dog dog("Lassie");
  return &dog;
}

私のライブラリは、エンドユーザーがアプリケーションの一部としてコンパイルするためのC++コードを生成します。生成されるコードは、スレッドセーフなクロスプラットフォーム方式で静的変数を初期化する必要があります。変数の初期化をミューテックスするために使用したいboost::call_onceのですが、エンドユーザーはBoostの依存関係にさらされています。

エンドユーザーに余分な依存関係を強いることなくこれを行う方法はありますか?

4

5 に答える 5

10

そのような静的初期化はスレッドセーフではないことは正しいです(コンパイラーがそれを何に変換するかについて説明している記事はこちらです)

現時点では、静的なシングルトンを初期化するための、スレッドセーフで移植可能な標準的な方法はありません。二重チェック ロックを使用できますが、潜在的に移植性のないスレッド ライブラリが必要です (ここでの説明を参照してください)。

スレッド セーフが必須である場合のいくつかのオプションを次に示します。

  1. 怠惰にならないでください (ロード済み): 静的初期化中に初期化します。静的初期化の順序が定義されていないため、別の静的がそのコンストラクターでこの関数を呼び出すと、問題になる可能性があります (こちらを参照)。
  2. ブースト(あなたが言ったように)またはロキを使用してください
  3. サポートされているプラ​​ットフォームで独自のシングルトンをロールします (スレッドの専門家でない限り、おそらく避けるべきです)
  4. アクセスが必要になるたびにミューテックスをロックします。これは非常に遅くなる可能性があります。

1 の例:

// in a cpp:
namespace {
    Dog dog("Lassie");
}

Dog* MyClass::BadMethod()
{
  return &dog;
}

4 の例:

Dog* MyClass::BadMethod()
{
  static scoped_ptr<Dog> pdog;
  {
     Lock l(Mutex);
     if(!pdog.get())
       pdog.reset(new Dog("Lassie"));
  }
  return pdog.get();
}
于 2009-06-27T05:55:01.730 に答える
4

これがあなたの意図するものかどうかはわかりませんが、pthread_once代わりに呼び出すことで、POSIX システムへの boost の依存関係を削除できます。Windows では別のことをしなければならないと思いますが、それを避けることが、そもそもブーストにスレッド ライブラリがあり、人々がそれに依存する代償を払う理由です。

「スレッドセーフ」で何かを行うことは、本質的にスレッドの実装に結び付けられています。プラットフォームに依存するメモリ モデルだけであっても、何かに依存する必要があります。純粋な C++03 では、言語の範囲外にあるスレッドについて何も想定することはまったくできません。

于 2009-06-27T09:55:33.263 に答える
3

スレッド セーフのためにミューテックスを必要としない方法の 1 つは、シングルトンを関数 static ではなくファイル static にすることです。

static Dog dog("Lassie");
Dog* MyClass::BadMethod()
{
  return &dog;
}

Dogインスタンスは、メイン スレッドが実行される前に初期化されます。ファイルの静的変数には初期化順序に関する有名な問題がありますが、Dog が別の翻訳単位で定義された他の静的変数に依存しない限り、これは問題になりません。

于 2009-06-27T05:47:05.190 に答える
2

あなたのような保護されていないリソースでスレッド化の問題が発生しないことを保証する唯一の方法は、スレッドが作成される前に"static Dog"すべてインスタンス化されることを要件にすることです。

MyInit()これは、他のことを行う前にメインスレッドで関数を呼び出す必要があることを文書化するだけの簡単なものです。次にMyInit()、それらのスタティックの 1 つを含む各タイプの 1 つのオブジェクトをインスタンス化して破棄するように構築します。

他の唯一の代替手段は、生成されたコードの使用方法に別の制限を設けることです (Boost、Win32 スレッドなどを使用します)。私の意見では、これらの解決策はどちらも受け入れられます。従う必要のあるルールを生成しても問題ありません。

ドキュメントに記載されているルールに従わない場合、すべての賭けは無効になります。初期化関数を呼び出すか、Boost に依存する必要があるというルールは、私にとっては不合理ではありません。

于 2009-06-27T05:43:45.367 に答える
2

私の知る限り、これが安全に行われ、ミューテックスやグローバルインスタンスの事前初期化なしで行われたのは、「スピンミューテックス」を使用してこれを行う方法について説明しているMatthew Wilson のImperfect C++だけです。私はそのコピーの近くにいないので、現時点ではこれ以上正確に伝えることはできません.

IIRC、 STLSoftライブラリ内でこれを使用する例がいくつかありますが、現時点ではどのコンポーネントを思い出せません。

于 2009-06-27T06:36:51.553 に答える