Đ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.
Xem http: // stackoverflow.com/questions/164496/how-can-i-create-an-thread-an toàn-singleton-pattern-in-windows – MerickOWA
@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ủ đề). –
@ 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