2

私はVisual Studio 2013を使用していますが、 "静的静的"機能はまだ実装されていません。スレッドセーフです。だから、代わりにローカル静的std :: once_flagとローカル静的ポインタを使用した静的変数のスレッドセーフ初期化

Foo& GetInstance() 
{ 
    static Foo foo; 
    return foo; 
} 

の私はこのような何か:

std::unique_ptr<Foo> gp_foo; 
std::once_flag g_flag; 

Foo& GetInstance() 
{ 
    std::call_once(g_flag, [](){ gp_foo = std::make_unique<Foo>(); });  
    return *gp_foo; 
} 

をしかし、私はgp_foog_flagグローバル変数(最初に、静的の初期化の順序に問題があるのアイデアを好きではありません変数を異なる翻訳単位に変換する;第2に、必要なときに、つまりGetInstance()に最初に呼び出した後にのみ変数を初期化したいので、次のコードを実装しました。

Foo& GetInstance() 
{ 
    // I replaced a smart pointer 
    // with a raw one just to be more "safe" 
    // not sure such replacing is really needed 
    static Foo *p_foo = nullptr; 

    static std::once_flag flag; 
    std::call_once(flag, [](){ p_foo = new Foo; });  
    return *p_foo; 
} 

これは動作しているようです(少なくともテストに合格したようです)が、スレッドセーフであるかどうかはわかりません。なぜなら、複数のスレッドで静的ローカル変数p_fooflagの初期化に同じ潜在的な問題があるからです。 nullptrの生ポインタの初期化とstd::once_flagの初期化は、Fooのコンストラクタを呼び出すより無邪気なようですが、本当に安全かどうかを知りたいと思います。

最後のコードスニペットに問題はありますか?

+0

httpsを返すことを示唆している://codereview.stackexchangeを。 com/ – Blacktempel

+0

[call_once on cppreference](http://en.cppreference.com/w/cpp/thread/call_once)を参照してください。特に箇条書きのポイント2 – Simple

+0

@Simple _グループ内の呼び出しは、上記の選択された関数の実行が正常に完了する前に戻りません。つまり、例外を介して終了しません._しかし、懸念事項はstd ::に関するものではありません。 call_onceではなく、これらの2行について 'static Foo * p_foo = nullptr; std :: once_flagフラグ; ' – undermind

答えて

0

Foo& GetInstance()が同じコンパイル単位の一部である場合、初期化順序が定義されているため、スレッドセーフです。

しかし、上記のようなものではなく、複数のコンパイルユニットがそれを参照している場合、初期化順序はFoo& GetInstance()への呼び出しの順序に依存し、複数のスレッドが関与する場合、その順序は未定義でスレッドセーフではありません。チェックする価値

+0

最後のスニペットでは、私はローカル静的変数しか持っていません。 – undermind

+0

はい、同じことが、 'Foo&GetInstance()'を同時に呼び出す2つのスレッドに対してtrue/reproducibleになることがあります。 – Griffin

+0

@undermindは 'C++ 11'でこれがそうであると言っています(https://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11)thisおそらくスレッドセーフです。今私はあまりよく分かっていないかもしれない。 – Griffin

1

は断然シングルトンオブジェクトの初期化に最も安定したアプローチがschwartz_counterです。グローバルオブジェクトの初期化順序にかかわらず、std::cincoutなどがどのように実装され、どのように動作するかです。

これは、すべてのバージョンのC++で機能します。

https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter

+0

ところで、ロード時にすべてのグローバル変数の初期化が同じスレッドから実行されることが保証されていますか? 2つの[Delay-Loaded DLLs](https://msdn.microsoft.com/en-us/library/151kt790.aspx)、たとえば関数_func1_の_lib1.dll_、関数_func2_の_lib2.dll_、および_func1_と_func2_は異なるスレッドから同時に呼び出されますか? – undermind

+0

いずれにしても、 'int'を' std :: atomic 'に置き換えることができるので、実際にはこの慣用句は役に立たないというわけではないので、最初の質問はちょっと好奇心から外れています。 – undermind

+0

@undermindシュワルツカウントされたオブジェクトは、初期化されていないグローバルがゼロになった後、mainが実行される前に初期化されます。 DLLでは、DllMainが実行される直前に発生します。これは、スレッドアタッチメントコードの前に発生すると考えられます。しかし、私は長い間DLLを使用していないので、チェックしなければならないでしょう。彼らに耐えられない! –

0

あなたの最後のコードスニペットは、ビューのスレッドセーフな初期点からで結構です。

しかし、を呼び出すスレッドでFooオブジェクトをどのように使用するのかは不明です。 非constオブジェクトへの参照を返すので、スレッドはFooオブジェクトを変更する可能性があります。 これに追加の同期が必要であることに注意してください(例:mutex

Fooオブジェクトが完全にそのコンストラクタによって初期化され、GetInstanceを呼び出すスレッドが唯一のオブジェクトから読み取るする場合は、そこには問題はありませんが、私はconst Foo &

関連する問題