2012-05-14 13 views
10

Rozglądam się za SO i MSDN, aby uzyskać odpowiedź na to pytanie, ale nie mogę znaleźć jasnej i ostatecznej odpowiedzi ...Czy statyczne zainicjowanie wątku jest bezpieczne przy użyciu VC2010?

Wiem, że jest w standardzie C++ 11 i że obecny Wersja GCC zachowuje się w ten sposób, ale czy VC2010 gwarantuje obecnie bezpieczeństwo wątku lokalnej inicjalizacji zmiennych statycznych?

tj .: Czy ten wątek jest bezpieczny w przypadku VC2010?

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

... A jeśli nie, to jaki jest obecny najlepszych praktyk, aby uzyskać wdrożenia singleton wątku bezpieczny w C++ z VC2010?

EDYCJA: Jak wskazała odpowiedź Chrisa Betti, VC2010 nie implementuje bezpieczeństwa wątków lokalnej zmiennej statycznej init.

+0

Zobacz http: // stackoverflow.com/questions/164496/how-can-i-create-a-thread-safe-singleton-pattern-in-windows – MerickOWA

+0

@MerickOWA: Ta data pochodzi z '08 i nie zapewnia jasnej i akceptowanej na całym świecie odpowiedzi. Plus to nawet nie obejmuje VC2010 (ze względu na wiek tematu). –

+0

@ IC3M4N VS2010 został zaimplementowany przed pojawieniem się C++ 11, jeśli nie implementuje bezpiecznej konstrukcji statycznych zmiennych lokalnych, pozostaje ci użycie technik, które istnieją od wielu lat. Nie widzę niczego, co nie ma zastosowania do VS2010 – MerickOWA

Odpowiedz

11

Od Visual Studio 2010'S documentation on Static:

przypisanie wartości do statycznej zmiennej lokalnej w wielowątkowych aplikacji nie jest wątku bezpieczne i nie polecamy go jako praktyka programowania.

Druga część pytania ma some good existing answers.

Zaktualizowano 22 listopada 2015:

Inni zweryfikowane, zwłaszcza, że ​​nie jest statyczna inicjalizacji wątku bezpieczne albo (patrz komentarz i inną odpowiedź).

squelart użytkownika na VS2015:

może warto dodać, że VS2015 w końcu dostaje to prawo: https://msdn.microsoft.com/en-au/library/hh567368.aspx#concurrencytable ("Magic statyka")

+0

Od wersji C++ 11 jest bezpieczna dla wątków. – Jagannath

+6

Uzgodniono, że C++ 11 wymaga, aby był bezpieczny dla wątków, ale to nie znaczy, że VS2010 implementuje to wymaganie. Zgodnie z ich dokumentacją wymóg ten nie jest spełniony. –

+0

@Jagannath Tak jak powiedziałem, wiem to. Pytałem o implementację VC2010 tej konkretnej funkcji C++ 11. –

7

Poniższy fragment kodu pokazuje „lokalnie scoped obiektu statycznego inicjalizacji”nie jest bezpieczny wątku:

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

wyjście będzie (oczywiście identyfikator będzie inny wątek na komputerze):

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

Przed pierwszych zwrotów wątku oznacza konstruktor Singleton nazywa i wrócił, drugi wątek uzyskać obiekt un-zainicjowany i nazwać to metoda członek (ponieważ obiekt statyczny jest w segmencie BSS, to będzie initilized do zera po ładowaczem załadować plik wykonywalny) i dostać złą wartość: 0.

Włączanie montaż wystawianie przez/FASC /Fastatic.asm dostanie kod montaż dla funkcji 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 : } 

W linii 10 ukryty symbol [? $ S1 @? 1 ?? getX @@ YAAAUX @@ XZ @ 4IA] jest globalnym wskaźnikiem (również w BSS), który sygnalizuje, czy singleton jest sprawdzony, czy nie, będzie zwrócony jako prawdziwy przez linię 14-17, tuż przed wywołaniem w ctor, to jest problem, to także wyjaśnia, dlaczego drugi wątek natychmiast uzyskał nie zainicjowany obiekt singleton i szczęśliwie nazywa się jego funkcją członka. W kompilatorze nie ma kodu powiązanego z bezpieczeństwem wątków.

+1

Dzisiaj sprawdziłem to z VS2013, (Niestety) wynik jest taki sam. – zhaorufei

Powiązane problemy