2010-04-04 34 views
56

Các mô hình thông thường cho một lớp singleton là một cái gì đó giống nhưhiệu quả thread-safe singleton trong C++

static Foo &getInst() 
{ 
    static Foo *inst = NULL; 
    if(inst == NULL) 
    inst = new Foo(...); 
    return *inst;  
} 

Tuy nhiên, đó là sự hiểu biết của tôi rằng giải pháp này không phải là thread-safe, vì 1) constructor Foo của có thể được gọi nhiều hơn hơn một lần (có thể hoặc có thể không quan trọng) và 2) inst có thể không được xây dựng hoàn chỉnh trước khi nó được trả về một chủ đề khác.

Một giải pháp là để bọc một mutex xung quanh toàn bộ phương pháp, nhưng sau đó tôi trả tiền cho chi phí đồng bộ hóa lâu sau khi tôi thực sự cần nó. Một giải pháp thay thế là một cái gì đó giống như

static Foo &getInst() 
{ 
    static Foo *inst = NULL; 
    if(inst == NULL) 
    { 
    pthread_mutex_lock(&mutex); 
    if(inst == NULL) 
     inst = new Foo(...); 
    pthread_mutex_unlock(&mutex); 
    } 
    return *inst;  
} 

Đây có phải là cách phù hợp để làm điều đó, hoặc có bất kỳ cạm bẫy nào mà tôi cần biết không? Ví dụ, có bất kỳ vấn đề khởi tạo tĩnh nào có thể xảy ra, tức là inst luôn được bảo đảm là NULL thì getInst đầu tiên được gọi là?

+5

Nhưng bạn không có thời gian để tìm một ví dụ và đánh dấu một cuộc bỏ phiếu kín? Tôi mới ra ngoài vào lúc này. – bmargulies

+1

có thể trùng lặp của http://stackoverflow.com/questions/6915/thread-safe-lazy-contruction-of-a-singleton-in-c – kennytm

+3

@bmargulies Không, người hỏi rõ ràng là không thể bị làm phiền, vậy tại sao nên TÔI? Tôi đã quyết định từ bỏ downvoting và đóng cửa như dupes, như tôi dường như là một trong số ít phiền toái để giữ crap ra khỏi SO. Và bạn có biết, lười biếng cảm thấy tốt! –

Trả lời

35

giải pháp của bạn được gọi là 'hai lần kiểm tra khóa' và cách bạn đã viết nó không phải là thread.

Điều này giải thích tại sao Meyers/Alexandrescu paper - nhưng giấy đó cũng bị hiểu lầm rộng rãi. Nó bắt đầu 'kiểm tra kép khóa là không an toàn trong meme C++' - nhưng kết luận thực tế của nó là đôi kiểm tra khóa trong C + + có thể được thực hiện một cách an toàn, nó chỉ đòi hỏi việc sử dụng các rào cản bộ nhớ ở một nơi không rõ ràng.

Bài báo có chứa mã giả cho thấy cách sử dụng các rào cản bộ nhớ để triển khai DLCP an toàn, vì vậy sẽ không khó để bạn khắc phục việc triển khai của mình.

+0

if (inst == NULL) {temp = new Foo (...); inst = temp;} Điều đó không đảm bảo rằng các nhà xây dựng đã hoàn thành trước khi inst được chỉ định? Tôi nhận ra nó có thể (và có lẽ sẽ được) tối ưu hóa đi, nhưng một cách hợp lý mà giải quyết vấn đề, không? – stu

+0

Điều đó không Không giúp đỡ bởi vì một trình biên dịch tuân thủ là miễn phí để sắp xếp lại các bài tập và các bước xây dựng như nó thấy phù hợp –

+0

Tôi đọc bài báo một cách cẩn thận, và có vẻ như đề nghị chỉ đơn giản là tránh DLCP với Singleton. ave để bay hơi heck ra khỏi lớp, và thêm các rào cản bộ nhớ (sẽ không ảnh hưởng đến hiệu quả là tốt?). Đối với nhu cầu thực tế, hãy sử dụng một khóa đơn, đơn giản và lưu vào bộ nhớ cache đối tượng bạn nhận được từ "GetInstance". – guyarad

8

Sử dụng pthread_once, được đảm bảo rằng hàm khởi tạo được chạy một lần một cách nguyên tử.

(Trên Mac OS X nó sử dụng một khóa quay. Không biết việc thực hiện của các nền tảng khác.)

2

TTBOMK, chỉ đảm bảo cách thread-safe để làm điều này mà không cần khóa sẽ được khởi tạo tất cả các bạn singletons trước bạn đã từng bắt đầu một chuỗi.

0

Phương án thay thế của bạn được gọi là "double-checked locking".

Có thể tồn tại mô hình đa luồng bộ nhớ trong đó nó hoạt động, nhưng POSIX không đảm bảo một

0

Triển khai singleton ACE sử dụng mẫu khóa được kiểm tra hai lần để đảm bảo an toàn cho chuỗi, bạn có thể tham khảo nó nếu bạn muốn.

Bạn có thể tìm thấy mã nguồn here.

61

Nếu bạn đang sử dụng C++ 11, đây là một cách đúng đắn để làm điều này:

Foo& getInst() 
{ 
    static Foo inst(...); 
    return inst; 
} 

Theo tiêu chuẩn mới không cần quan tâm đến vấn đề này nữa. Khởi tạo đối tượng sẽ chỉ được thực hiện bởi một luồng, các luồng khác sẽ đợi cho đến khi nó hoàn tất. Hoặc bạn có thể sử dụng lệnh std :: call_once.(Biết thêm here)

+1

Đây là giải pháp C++ 11 Tôi hy vọng mọi người sẽ thực hiện. – Alex

+8

Đáng buồn thay, đây không phải là chủ đề an toàn trong VS2013, hãy xem "Magic Statics" tại đây: http://msdn.microsoft.com/en-gb/library/hh567368.aspx –

+3

VS 14 dường như sửa lỗi này - http: // blogs.msdn.com/b/vcblog/archive/2014/06/03/visual-studio-14-ctp.aspx –

7

Herb Sutter talks about the double-checked locking in CppCon 2014.

Dưới đây là đoạn code tôi thực hiện trong C++ 11 dựa trên rằng:

class Foo { 
public: 
    static Foo* Instance(); 
private: 
    Foo() {} 
    static atomic<Foo*> pinstance; 
    static mutex m_; 
}; 

atomic<Foo*> Foo::pinstance { nullptr }; 
std::mutex Foo::m_; 

Foo* Foo::Instance() { 
    if(pinstance == nullptr) { 
    lock_guard<mutex> lock(m_); 
    if(pinstance == nullptr) { 
     pinstance = new Foo(); 
    } 
    } 
    return pinstance; 
} 

bạn cũng có thể kiểm tra chương trình đầy đủ ở đây: http://ideone.com/olvK13

+1

Tại sao thực hiện kiểm tra lại khi bạn đã có C++ 11? – Etherealone

+1

@Etherealone đề xuất của bạn là gì? – qqibrow

+1

Một đơn giản 'static Foo foo;' và 'return &foo;' bên trong hàm instance sẽ là đủ; 'khởi tạo' tĩnh là thread-safe trong C++ 11. Thích tham chiếu đến con trỏ mặc dù. – Etherealone