Mic*_*ltu 5 c++ c++11 visual-studio-2012
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");
}
Run Code Online (Sandbox Code Playgroud)
Visual Studio 2012上的上述程序的输出很可能是:
0
5
Run Code Online (Sandbox Code Playgroud)
我需要解决这个问题,我试图找到一种方法,只使用函数本地静态(没有全局或类级静态).
我最初的想法是使用互斥锁,但它遇到了静态初始化线程安全的相同问题.如果我在foo中有一个静态的st :: mutex,那么第二个线程可能会在它处于无效状态时获得互斥锁的副本.
另一个选择是添加一个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';
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,两个线程是否有可能通过自旋锁,或者它是否只保证其中一个?不幸的是,我想不出一个简单的方法来测试这个,因为我不能在atomic_flag初始化器中放入一些东西来减慢速度,就像我可以用类一样.但是,我想确保我的程序在蓝色月亮中不会崩溃,因为我做了一个无效的假设.
C++ 11的第6.7.4节规定具有静态存储持续时间的变量是初始化的线程安全的:
如果控制在初始化变量时同时进入声明,则并发执行应等待初始化完成.
但VC++ 2012或2013 Preview都没有实现这一点,所以是的,你需要一些保护来使你的函数线程安全.
C++ 11 ATOMIC_FLAG_INIT在第29.7.4节中也说了这个:
ATOMIC_FLAG_INIT应以这样的方式定义宏,使其可用于将类型的对象初始化atomic_flag为清除状态.对于静态持续时间对象,该初始化应该是静态的.
VC++ 确实恰好实现了这一点.ATOMIC_FLAG_INIT是0在VC++和VC++零初始化所有静态在应用程序启动,而不是在函数调用.因此,您对此的使用是安全的,并且没有竞争初始化lock.
测试代码:
struct nontrivial
{
nontrivial() : x(123) {}
int x;
};
__declspec(dllexport) int next_x()
{
static nontrivial x;
return ++x.x;
}
__declspec(dllexport) int next_x_ts()
{
static std::atomic_flag flag = ATOMIC_FLAG_INIT;
while(flag.test_and_set());
static nontrivial x;
flag.clear();
return ++x.x;
}
Run Code Online (Sandbox Code Playgroud)
next_x:
mov eax, cs:dword_1400035E4
test al, 1 ; checking if x has been initialized.
jnz short loc_140001021 ; if it has, go down to the end.
or eax, 1
mov cs:dword_1400035E4, eax ; otherwise, set it as initialized.
mov eax, 7Bh
inc eax ; /O2 is on, how'd this inc sneak in!?
mov cs:dword_1400035D8, eax ; init x.x to 124 and return.
retn
loc_140001021:
mov eax, cs:dword_1400035D8
inc eax
mov cs:dword_1400035D8, eax
retn
Run Code Online (Sandbox Code Playgroud)
next_x_ts:
loc_140001032:
lock bts cs:dword_1400035D4, 0 ; flag.test_and_set().
jb short loc_140001032 ; spin until set.
mov eax, cs:dword_1400035E0
test al, 1 ; checking if x has been initialized.
jnz short loc_14000105A ; if it has, go down to end.
or eax, 1 ; otherwise, set is as initialized.
mov cs:dword_1400035E8, 7Bh ; init x.x with 123.
mov cs:dword_1400035E0, eax
loc_14000105A:
lock btr cs:dword_1400035D4, 0 ; flag.clear().
mov eax, cs:dword_1400035E8
inc eax
mov cs:dword_1400035E8, eax
retn
Run Code Online (Sandbox Code Playgroud)
你可以在这里看到next_x绝对不是线程安全的,但next_x_ts永远不会初始化flag变量cs:dword_1400035D4- 它在应用程序启动时被零初始化,所以没有比赛并且next_x_ts是线程安全的.
| 归档时间: |
|
| 查看次数: |
1532 次 |
| 最近记录: |