Singleton(Meyers 'Singleton) 스레드 의 지연 초기화를 사용하는 다음 구현은 안전합니까?
static Singleton& instance()
{
static Singleton s;
return s;
}
그렇지 않다면 왜 그리고 어떻게 스레드를 안전하게 만드는가?
Singleton(Meyers 'Singleton) 스레드 의 지연 초기화를 사용하는 다음 구현은 안전합니까?
static Singleton& instance()
{
static Singleton s;
return s;
}
그렇지 않다면 왜 그리고 어떻게 스레드를 안전하게 만드는가?
답변:
에서 C ++ (11) , 그것은 스레드 안전합니다. 에 따르면 표준 , §6.7 [stmt.dcl] p4:
변수가 초기화되는 동안 제어가 동시에 선언에 들어가면 동시 실행 은 초기화가 완료 될 때까지 기다려야 합니다.
이 기능에 대한 GCC 및 VS 지원 ( MSN에서 Magic Statics 라고도 하는 동시성 동적 초기화 및 파괴 )은 다음과 같습니다.
의견을 주신 @Mankarse와 @olen_gam에게 감사합니다.
에서 C ++ 03 이 코드는 스레드로부터 안전하지 않았다. "C ++ 및 이중 검사 잠금의 위험" 이라는 Meyers의 기사 에서 스레드 안전 패턴 구현에 대해 설명하고 결론은 (C ++ 03에서) 인스턴스화 방법에 대한 전체 잠금입니다. 는 기본적으로 모든 플랫폼에서 적절한 동시성을 보장하는 가장 간단한 방법이며, 대부분의 이중 검사 잠금 패턴 변형은 특정 아키텍처 에서 경쟁 조건을 겪을 수 있습니다 ( 명령이 전략적으로 메모리 장벽을 배치하지 않는 한).
스레드 안전하지 않은 이유에 대한 질문에 대답하기 위해 첫 번째 호출이에 instance()대한 생성자를 호출해야 하기 때문이 아닙니다 Singleton s. 스레드 안전을 위해서는 이것이 중요한 섹션에서 발생해야하지만, 표준에서는 중요한 섹션을 취할 필요가 없습니다 (현재까지의 표준은 스레드에서 완전히 침묵합니다). 컴파일러는 종종 간단한 검사와 정적 부울 증가를 사용하여이를 구현하지만 중요 섹션에는 없습니다. 다음 의사 코드와 같은 것 :
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)));
}
여기 간단한 스레드 안전 싱글 톤 (Windows 용)이 있습니다. 컴파일러는 Windows CRITICAL_SECTION 객체에 간단한 클래스 래퍼를 사용하여 컴파일러가 CRITICAL_SECTION이전을 자동으로 초기화하도록 할 수 있습니다 main(). 중요 섹션이 개최 될 때 발생할 수있는 예외를 처리 할 수있는 진정한 RAII 임계 섹션 클래스가 이상적으로 사용되지만이 답변의 범위를 벗어납니다.
기본 작업은 인스턴스 Singleton가 요청되고 잠금이 수행되고 필요한 경우 싱글 톤이 생성 된 다음 잠금이 해제되고 싱글 톤 참조가 반환되는 것입니다.
#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;
}
사람-그것은 "더 나은 세상을 만들기"위해 많은 쓰레기입니다.
이 구현의 주요 단점은 다음과 같습니다 (버그가 해결되지 않은 경우).
new Singleton()가 발생, 잠금이 해제되지 않습니다. 이것은 내가 가지고있는 간단한 것 대신 진정한 RAII 잠금 객체를 사용하여 해결할 수 있습니다. 또한 Boost와 같은 것을 사용하여 잠금을위한 플랫폼 독립적 인 래퍼를 제공하는 경우 물건을 이식 할 수 있습니다.main()호출 된 후 요청 될 때 스레드 안전성이 보장됩니다. 그 전에 호출하면 (정적 객체의 초기화와 같이) 초기화되지 않았기 때문에 작동 CRITICAL_SECTION하지 않을 수 있습니다.new Singleton()던져?
new Singleton()던져 지면 분명히 잠금에 문제가 있습니다. lock_guardBoost 와 같은 적절한 RAII 잠금 클래스를 사용해야합니다 . 나는 예제가 다소 독립적이기를 원했고, 이미 약간의 괴물이어서 예외 안전을 피했습니다 (그러나 불렀습니다). 어쩌면이 코드가 부적절한 곳에서 잘라 붙여 넣지 않도록 수정해야 할 수도 있습니다.
다음 구현 [...] 스레드는 안전한가요?
대부분의 플랫폼에서 스레드 안전하지 않습니다. (C ++ 표준은 스레드에 대해 알지 못하므로 법적으로 법적 여부를 말하지 않는다는 일반적인 고지 사항을 추가하십시오.)
그렇지 않다면 왜 [...]?
그렇지 않은 이유는 둘 이상의 스레드가 동시에 s'생성자를 실행하는 것을 방해하지 않기 때문입니다 .
스레드를 안전하게 만드는 방법?
Scott Meyers와 Andrei Alexandrescu의 "C ++ 및 이중 검사 잠금 위험" 은 스레드 안전 싱글 톤 주제에 대한 훌륭한 논문입니다.