development

Meyers의 Singleton 패턴 스레드 구현은 안전합니까?

big-blog 2020. 7. 4. 09:30
반응형

Meyers의 Singleton 패턴 스레드 구현은 안전합니까?


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_SECTIONbefore를 자동으로 초기화하도록 할 수 있습니다 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;
}

사람-그것은 "더 나은 세상을 만들기"위해 많은 쓰레기입니다.

이 구현의 주요 단점은 (버그가 해결되지 않은 경우) 다음과 같습니다.

  • if new Singleton() throws, the lock won't be released. This can be fixed by using a true RAII lock object instead of the simple one I have here. This can also help make things portable if you use something like Boost to provide a platform independent wrapper for the lock.
  • this guarantees thread safety when the Singleton instance is requested after main() is called - if you call it before then (like in a static object's initialization) things might not work because the CRITICAL_SECTION might not be initialized.
  • a lock must be taken each time an instance is requested. As I said, this is a simple thread safe implementation. If you need a better one (or want to know why things like the double-check lock technique is flawed), see the papers linked to in Groo's answer.

Looking at the next standard (section 6.7.4), it explians how static local initialization is thread safe. So once that section of standard is widely implemented, Meyer's Singleton will be the preferred implementation.

I disagree with many answers already. Most compilers already implement static initialization this way. The one notable exception is Microsoft Visual Studio.


The correct answer depends on your compiler. It can decide to make it threadsafe; it's not "naturallly" threadsafe.


Is the following implementation [...] thread safe?

On most platforms, this is not thread-safe. (Append the usual disclaimer explaining that the C++ standard doesn't know about threads, so, legally, it doesn't say whether it is or not.)

If not, why [...]?

The reason it isn't is that nothing prevents more than one thread from simultaneously executing s' constructor.

how to make it thread safe?

"C++ and the Perils of Double-Checked Locking" by Scott Meyers and Andrei Alexandrescu is a pretty good treatise on the subject of thread-safe singletons.


As MSalters said: It depends on the C++ implementation you use. Check the documentation. As for the other question: "If not, why?" -- The C++ standard doesn't yet mention anything about threads. But the upcoming C++ version is aware of threads and it explicitly states that the initialization of static locals is thread-safe. If two threads call such a function, one thread will perform an initialization while the other will block & wait for it to finish.

참고URL : https://stackoverflow.com/questions/1661529/is-meyers-implementation-of-the-singleton-pattern-thread-safe

반응형