Вопрос по visual-c++, c++11, multithreading, c++ – Является ли статическая инициализация потокобезопасной с VC2010?

10

Я искал ответ на этот вопрос в SO и MSDN, но не могу найти четкого и окончательного ответа ...

Я знаю, что это стандарт C ++ 11 и что текущая версия GCC ведет себя таким образом, но гарантирует ли VC2010 в настоящее время безопасность потоков при инициализации локальной статической переменной?

т. е. Является ли этот потокобезопасным с VC2010?

    static S& getInstance()
    {
        static S instance;
        return instance;
    }

... А если нет, какова текущая лучшая практика для получения поточно-ориентированной одноэлементной реализации в C ++ с VC2010?

EDIT: Как указано в ответе Криса Бетти, VC2010 не реализует многопотоковую защиту локальной статической переменной init.

Видеть / Stackoverflow.com вопросы / 164496 / ... MerickOWA
@ MerickOWA: это восходит к '08 и не дает четкого и общепринятого ответа. Плюс это даже не покрывает VC2010 (из-за возраста темы). Matt Fortier
@ IC3M4N VS2010 был реализован до выхода C ++ 11. Если он не реализует поточно-ориентированное построение статических локальных переменных, то у вас остались методы, которые использовались много лет назад. Я не вижу ничего, что не относится к VS2010 MerickOWA
@ IC3M4N, чтобы быть ясным, ответ Криса дает ссылку на хорошие общие ответы, я просто пытался дать ссылку на более конкретные ответы Windows на вопрос. MerickOWA
@ MerickOWA Я просто указывал, что это не совсем то, что я искал. Спасибо за вашу помощь, это ценится. Matt Fortier

Ваш Ответ

2   ответа
11

документация по статике:

Присвоение значения статической локальной переменной в многопоточном приложении не является потокобезопасным, и мы не рекомендуем его в качестве практики программирования.

Во второй части твоего вопроса есть некоторые хорошие существующие ответы.

Обновлено 22 ноября 2015 г .:

Другие, в частности, убедились, что статическая инициализация также не является поточно-ориентированной (см. Комментарий и другой ответ).

Пользователь скуллара на VS2015:

Вы можете добавить, что VS2015 наконец-то понял все правильно:https: //msdn.microsoft.com/en-au/library/hh567368.aspx#concurrencytabl ("Волшебная статика")

Так как c ++ 11, это потокобезопасный. Jagannath
Согласитесь, что c ++ 11 требует, чтобы он был поточно-ориентированным, но это не означает, что VS2010 реализует это требование. Согласно их документации, требование не выполнено. Chris Betti
@ Jagannath Как я уже сказал, я знаю это. Я спрашивал о реализации этой конкретной функции C ++ 11 на VC2010. Matt Fortier
Это не значит, чтостроительств сказал не безопасно. Только назначение. Puppy
@ DeadMG: Хороший вопрос, но, честно говоря, можно предположить, что если присвоение не является поточно-ориентированным, то и инициализация тоже не будет. Matt Fortier
7

что «инициализация статического объекта в локальной области» НЕ является поточно-ориентированной:

#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;
}

Вывод будет (конечно, идентификатор потока на вашем компьютере будет другим):

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 ():

01:  [email protected]@[email protected]@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 [email protected][email protected]@[email protected]@[email protected]
11:    00008    83 e0 01     and     eax, 1
12:    0000b    75 2b        jne     SHORT [email protected]
13:    0000d    8b 0d 00 00 00
14:     00       mov     ecx, DWORD PTR [email protected][email protected]@[email protected]@[email protected]
15:    00013    83 c9 01     or  ecx, 1
16:    00016    89 0d 00 00 00
17:     00       mov     DWORD PTR [email protected][email protected]@[email protected]@[email protected], ecx
18:    0001c    68 e8 03 00 00   push    1000           ; 000003e8H
19:    00021    b9 00 00 00 00   mov     ecx, OFFSET [email protected][email protected]@[email protected]@[email protected]@A
20:    00026    e8 00 00 00 00   call    [email protected]@[email protected]@Z      ; X::X
21:    0002b    68 00 00 00 00   push    OFFSET [email protected][email protected]@[email protected]@[email protected] ; `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:  [email protected]:
25:  
26:  ; 22   :   return static_x;
27:  
28:    00038    b8 00 00 00 00   mov     eax, OFFSET [email protected][email protected]@[email protected]@[email protected]@A
29:  
30:  ; 23   : }

В строке 10 загадочный символ [? $ S1 @? 1 ?? getX @@ YAAAUX @@ XZ @ 4IA] является глобальным индикатором (также в BSS), который указывает, является ли синглтон ctored или нет, он будет помечен как true в строке 14-17, непосредственно перед вызовом в ctor, это проблема, это также объясняет, почему второй поток немедленно получил неинициализированный одноэлементный объект и с радостью вызвал свою функцию-член. Там нет никакого кода, связанного с безопасностью потока, вставленного компилятором.

Сегодня я проверил это с VS2013, (к сожалению) результат тот же. zhaorufei

Похожие вопросы