首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用VC2010静态init线程安全吗?

使用VC2010静态init线程安全吗?
EN

Stack Overflow用户
提问于 2012-05-14 15:03:21
回答 2查看 4.9K关注 0票数 10

我一直在四处寻找这个问题的答案,但似乎找不到一个明确和最终的答案.

我知道它在C++11标准中,现在GCC版本也是这样,但是VC2010目前是否保证本地静态变量初始化的线程安全?

也就是说: VC2010的线程安全吗?

代码语言:javascript
复制
    static S& getInstance()
    {
        static S instance;
        return instance;
    }

如果不是...And,那么在C++中使用VC2010获取线程安全的单例实现的最佳实践是什么?

编辑:正如Chris的答案所指出的,VC2010没有实现本地静态变量init.的线程安全性。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-05-14 15:09:35

来自Visual 2010的静态文档

在多线程应用程序中为静态局部变量分配值并不是线程安全的,我们不建议将其作为编程实践。

你问题的第二部分是一些现有的好答案

2015年11月22日更新:

其他人具体地验证了静态初始化也不是线程安全(请参阅注释和其他答案)。

用户在VS2015上的静音:

你可能想说VS2015终于把它做对了:https://msdn.microsoft.com/en-au/library/hh567368.aspx#concurrencytable (“魔术静力学”)

票数 11
EN

Stack Overflow用户

发布于 2012-06-21 01:32:36

下面的代码片段显示“本地作用域静态对象初始化”不是线程安全的:

代码语言:javascript
复制
#include <windows.h>
#include <stdio.h>
#include <process.h>
struct X {
    ~X() { puts("~X()"); }
    int i_ ;
    void print(void) {
        printf("thread id=%u, i = %d\n", GetCurrentThreadId(), i_);
    }
    X(int i) {
        puts("begin to sleep 10 seconds");
        Sleep(1000 * 10);
        i_ = i;
        printf("X(int) i = %d\n", i_);
        puts("end");
    }
};

X & getX()
{
    static X static_x(1000);
    return static_x;
}

void thread_proc(void *)
{
    X & x = getX();
    x.print();
}

int main(int argc, char *argv[])
{
    HANDLE all_threads[2] = {};
    all_threads[0] = HANDLE( _beginthread(thread_proc, 0, 0) );
    printf("First thread Id: %u\n", GetThreadId(all_threads[0]) );
    Sleep(1000);
    all_threads[1] = HANDLE( _beginthread(thread_proc, 0, 0) );
    printf("Second thread Id: %u\n", GetThreadId(all_threads[1]) );
    WaitForMultipleObjects( _countof(all_threads), all_threads, TRUE, 1000 * 20);
    puts("main exit");
    return 0;
}

输出将是(当然,线程id在您的机器上是不同的):

代码语言:javascript
复制
First thread Id: 20104
begin to sleep 10 seconds
Second thread Id: 20248
thread id=20248, i = 0
X(int) i = 4247392
end
thread id=20104, i = 1000
main exit
~X()

在第一个线程返回之前(这意味着调用并返回单例的ctor ),第二个线程将获得未初始化的对象并调用它的成员方法(因为静态对象位于BSS段中,因此在加载加载可执行文件后它将被初始化为零)并得到错误的值: 0。

/FAsc /Fastatic.asm打开程序集清单将获得函数getX()的汇编代码:

代码语言:javascript
复制
01:  ?getX@@YAAAUX@@XZ PROC                 ; getX
02:  
03:  ; 20   : {
04:  
05:    00000    55       push    ebp
06:    00001    8b ec        mov     ebp, esp
07:  
08:  ; 21   :   static X static_x(1000);
09:  
10:    00003    a1 00 00 00 00   mov     eax, DWORD PTR ?$S1@?1??getX@@YAAAUX@@XZ@4IA
11:    00008    83 e0 01     and     eax, 1
12:    0000b    75 2b        jne     SHORT $LN1@getX
13:    0000d    8b 0d 00 00 00
14:     00       mov     ecx, DWORD PTR ?$S1@?1??getX@@YAAAUX@@XZ@4IA
15:    00013    83 c9 01     or  ecx, 1
16:    00016    89 0d 00 00 00
17:     00       mov     DWORD PTR ?$S1@?1??getX@@YAAAUX@@XZ@4IA, ecx
18:    0001c    68 e8 03 00 00   push    1000           ; 000003e8H
19:    00021    b9 00 00 00 00   mov     ecx, OFFSET ?static_x@?1??getX@@YAAAUX@@XZ@4U2@A
20:    00026    e8 00 00 00 00   call    ??0X@@QAE@H@Z      ; X::X
21:    0002b    68 00 00 00 00   push    OFFSET ??__Fstatic_x@?1??getX@@YAAAUX@@XZ@YAXXZ ; `getX'::`2'::`dynamic atexit destructor for 'static_x''
22:    00030    e8 00 00 00 00   call    _atexit
23:    00035    83 c4 04     add     esp, 4
24:  $LN1@getX:
25:  
26:  ; 22   :   return static_x;
27:  
28:    00038    b8 00 00 00 00   mov     eax, OFFSET ?static_x@?1??getX@@YAAAUX@@XZ@4U2@A
29:  
30:  ; 23   : }

在第10行,$s1@1??getX@YAAAUX@@XZ@4IA是全局指示符(也是BSS中的),它标志着单例是否被捕获,它将被第14-17行平版,在调用ctor之前,这就是问题所在,这也解释了为什么第二个线程立即获得了un-初始化的单例对象,并高兴地调用它的成员函数。编译器没有插入与线程安全相关的代码。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10585928

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档