Visual Studio 2012 は、スレッド セーフな静的初期化のための C++11 標準を実装していません ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm )。スレッドセーフな方法で初期化されることを保証する必要がある関数 local static があります。以下は、Visual Studio 2012 ではスレッド セーフではありません。
struct MyClass
{
int a;
MyClass()
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
a = 5;
}
};
void foo()
{
static MyClass instance;
std::cout << instance.a << '\n';
}
int main()
{
std::thread a(foo);
std::thread b(foo);
a.join();
b.join();
system("pause");
}
Visual Studio 2012 での上記のプログラムの出力は、次のようになります。
0
5
私はこの問題を回避する必要があり、関数ローカル静的のみ (グローバルまたはクラス レベル静的なし) でそれを行う方法を見つけようとしています。
私の最初の考えは、ミューテックスを使用することでしたが、静的初期化スレッドの安全性という同じ問題に悩まされていました。foo 内に静的な st::mutex がある場合、2 番目のスレッドが無効な状態にある間にミューテックスのコピーを取得する可能性があります。
もう 1 つのオプションは、std::atomic_flag スピンロックを追加することです。問題は、Visual Studio 2012 で std::atomic_flag 初期化スレッド セーフですか?
void foo()
{
// is this line thread safe?
static std::atomic_flag lock = ATOMIC_FLAG_INIT;
// spin lock before static construction
while (lock.test_and_set(std::memory_order_acquire));
// construct an instance of MyClass only once
static MyClass instance;
// end spin lock
lock.clear(std::memory_order_release);
// the following is not thread safe
std::cout << instance.a << '\n';
}
上記のコードで、両方のスレッドがスピン ロックを通過することは可能ですか?それとも、どちらか一方だけが通過することが保証されていますか? 残念ながら、クラスでできるように、atomic_flag 初期化子の中に何かを入れて速度を落とすことができないため、これをテストする簡単な方法は考えられません。ただし、無効な仮定を行ったために、ブルームーンでプログラムが一度もクラッシュしないようにしたいと考えています。