Để trả lời câu hỏi của bạn về lý do tại sao nó không an toàn, không phải vì cuộc gọi đầu tiên đến instance()
phải gọi hàm tạo cho Singleton s
. Để trở thành chủ đề, điều này sẽ phải xảy ra trong một phần quan trọng, và không có yêu cầu trong tiêu chuẩn rằng một phần quan trọng được thực hiện (tiêu chuẩn cho đến nay hoàn toàn im lặng về các chủ đề). Các trình biên dịch thường thực hiện điều này bằng cách sử dụng một phép kiểm tra đơn giản và gia tăng của một boolean tĩnh - nhưng không phải trong một phần quan trọng. Nội dung như mã giả sau đây:
static Singleton& instance()
{
static bool initialized = false;
static char s[sizeof(Singleton)];
if (!initialized) {
initialized = true;
new(&s) Singleton(); // call placement new on s to construct it
}
return (*(reinterpret_cast<Singleton*>(&s)));
}
Vì vậy, đây là một Singleton đơn giản an toàn chỉ dành cho Windows. Nó sử dụng một trình bao bọc lớp đơn giản cho đối tượng Windows CRITICAL_SECTION để chúng ta có thể có trình biên dịch tự động khởi tạo CRITICAL_SECTION
trước khi gọi là main()
. Lý tưởng một lớp phần quan trọng RAII thực sự sẽ được sử dụng mà có thể đối phó với các ngoại lệ có thể xảy ra khi phần quan trọng được tổ chức, nhưng đó là vượt ra ngoài phạm vi của câu trả lời này.
Hoạt động cơ bản là khi một phiên bản yêu cầu Singleton
được yêu cầu, khóa được thực hiện, Singleton được tạo nếu cần, sau đó khóa được giải phóng và tham chiếu Singleton được trả về.
#include <windows.h>
class CritSection : public CRITICAL_SECTION
{
public:
CritSection() {
InitializeCriticalSection(this);
}
~CritSection() {
DeleteCriticalSection(this);
}
private:
// disable copy and assignment of CritSection
CritSection(CritSection const&);
CritSection& operator=(CritSection const&);
};
class Singleton
{
public:
static Singleton& instance();
private:
// don't allow public construct/destruct
Singleton();
~Singleton();
// disable copy & assignment
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
static CritSection instance_lock;
};
CritSection Singleton::instance_lock; // definition for Singleton's lock
// it's initialized before main() is called
Singleton::Singleton()
{
}
Singleton& Singleton::instance()
{
// check to see if we need to create the Singleton
EnterCriticalSection(&instance_lock);
static Singleton s;
LeaveCriticalSection(&instance_lock);
return s;
}
Man - đó là rất nhiều thứ để "tạo nên một toàn cầu tốt hơn".
Những nhược điểm chính để implemention này (nếu tôi đã không để một số lỗi lọt qua) là:
- nếu
new Singleton()
ném, khóa sẽ không được phát hành. Điều này có thể được cố định bằng cách sử dụng một đối tượng khóa RAII thực sự thay vì đối tượng khóa đơn giản mà tôi có ở đây. Điều này cũng có thể giúp làm cho mọi thứ di động nếu bạn sử dụng một cái gì đó như Boost để cung cấp một nền tảng độc lập wrapper cho khóa.
- điều này đảm bảo an toàn luồng khi yêu cầu Singleton sau khi gọi
main()
- nếu bạn gọi nó trước đó (như khởi tạo đối tượng tĩnh) mọi thứ có thể không hoạt động vì CRITICAL_SECTION
có thể không được khởi tạo.
- một khóa phải được thực hiện mỗi khi một yêu cầu được yêu cầu. Như tôi đã nói, đây là một chuỗi thực hiện an toàn đơn giản. Nếu bạn cần một cái tốt hơn (hoặc muốn biết tại sao những thứ như kỹ thuật khóa kiểm tra kép là thiếu sót), hãy xem papers linked to in Groo's answer.
Nguồn
2009-11-02 15:00:10
Ai đó có thể giải thích tại sao đây không phải là chủ đề an toàn. Các bài viết được đề cập trong các liên kết thảo luận về an toàn luồng bằng cách sử dụng một triển khai thay thế (sử dụng biến con trỏ tức là tĩnh Singleton * pInstance). – Ankur
Xem: http: // stackoverflow.com/questions/449436/singleton-instance-declaration-as-static-biến-of-getinstance-method/449823 # 449823 –
Xem: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/ 1008289 # 1008289 –