Visual Studio 2012 没有实现线程安全静态初始化的 C++11 标准 ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm )。我有一个本地静态函数,我需要保证它会以线程安全的方式进行初始化。以下在 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 ,则第二个线程可能会在它处于无效状态时获得互斥锁的副本。
另一种选择是添加一个 std::atomic_flag 自旋锁。问题是,std::atomic_flag 初始化线程在 Visual Studio 2012 中是否安全?
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 初始化程序中放入一些东西来减慢它,就像我在一个类中可以做到的那样。但是,我想确保我的程序不会因为我做了一个无效的假设而崩溃一次。