2012-05-14 29 views
10

Tôi đã tìm kiếm tất cả xung quanh SO và MSDN để có câu trả lời cho câu hỏi này, nhưng dường như không thể tìm thấy câu trả lời rõ ràng và cuối cùng ...Có an toàn cho init thread an toàn với VC2010 không?

Tôi biết rằng đó là tiêu chuẩn C++ 11 và hiện tại Phiên bản GCC hoạt động theo cách này, nhưng hiện tại VC2010 có đảm bảo an toàn luồng của một khởi tạo biến tĩnh cục bộ không?

tức là: Chủ đề này có an toàn với VC2010 không?

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

... Và nếu không, thực tiễn tốt nhất hiện tại để thực hiện đơn lẻ an toàn trong C++ với VC2010 là gì?

CHỈNH SỬA: Như được chỉ ra bởi câu trả lời của Chris Betti, VC2010 không thực hiện an toàn luồng của biến tĩnh cục bộ init.

+0

Xem http: // stackoverflow.com/questions/164496/how-can-i-create-an-thread-an toàn-singleton-pattern-in-windows – MerickOWA

+0

@MerickOWA: Ngày này bắt đầu từ năm 2008 và không cung cấp câu trả lời rõ ràng và được chấp nhận toàn cầu. Cộng với nó thậm chí không bao gồm VC2010 (do tuổi chủ đề). –

+0

@ IC3M4N VS2010 đã được triển khai trước khi C++ 11 xuất hiện, Nếu nó không thực hiện xây dựng chuỗi an toàn các biến tĩnh cục bộ, thì bạn còn lại với việc sử dụng các kỹ thuật đã tồn tại trong nhiều năm. Tôi không thấy bất cứ điều gì không áp dụng cho VS2010 – MerickOWA

Trả lời

11

Từ Visual Studio 2010 của documentation on Static:

Gán một giá trị cho một biến địa phương tĩnh trong một ứng dụng đa luồng không phải là thread an toàn và chúng tôi không khuyên nó như là một thực hành lập trình.

Phần thứ hai của câu hỏi của bạn có some good existing answers.

Cập nhật 22 tháng 11 năm 2015:

Những người khác đã xác nhận, cụ thể, mà khởi tạo tĩnh không phải là thread an toàn hoặc (xem bình luận và câu trả lời khác).

tài squelart trên VS2015:

bạn có thể muốn thêm rằng VS2015 cuối cùng được nó ngay: https://msdn.microsoft.com/en-au/library/hh567368.aspx#concurrencytable ("Magic tĩnh")

+0

Vì C++ 11, nó là chủ đề an toàn. – Jagannath

+6

Đồng ý rằng C++ 11 yêu cầu nó phải là chủ đề an toàn, nhưng điều đó không có nghĩa là VS2010 thực hiện yêu cầu này. Theo tài liệu của họ, yêu cầu không được đáp ứng. –

+0

@Jagannath Như đã nói trong câu hỏi, tôi biết điều đó. Tôi đã hỏi về việc triển khai VC2010 tính năng C++ 11 cụ thể này. –

7

Đoạn mã sau đây cho thấy "địa phương scoped đối tượng tĩnh initialisation "is NOT thread-safe:

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

Đầu ra sẽ là (tất nhiên id luồng sẽ khác trên máy của bạn):

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

Trước khi trở về chủ đề đầu tiên có nghĩa là ctor của singleton được gọi và trả về, sợi thứ hai có được đối tượng un-khởi tạo và gọi nó là phương pháp thành viên (vì đối tượng tĩnh là trong phân khúc BSS, nó sẽ được initilized bằng không sau khi nạp nạp thực thi) và nhận được giá trị sai: 0.

Bật lắp ráp niêm yết bằng/FASC /Fastatic.asm sẽ lấy mã lắp ráp cho chức năng 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]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: [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 : } 

Tại dòng 10 biểu tượng bí ẩn [? $ S1 @? 1 ?? getX @@ YAAAUX @@ XZ @ 4IA] là chỉ báo toàn cầu (cũng trong BSS) có cờ cho dù singleton được ctored hay không, nó sẽ được flaged là đúng bởi dòng 14-17, ngay trước khi gọi vào ctor, đó là vấn đề, điều này cũng giải thích lý do tại sao luồng thứ hai ngay lập tức nhận đối tượng singleton chưa được khởi tạo và vui vẻ gọi đó là hàm thành viên. Không có mã liên quan đến luồng an toàn được trình biên dịch chèn vào.

+1

Hôm nay tôi đã kiểm tra điều này với VS2013, (Thật không may) kết quả là như nhau. – zhaorufei

Các vấn đề liên quan