Windows에서 스레드 안전 싱글 톤 패턴을 어떻게 만들 수 있습니까?

StackOverflow https://stackoverflow.com/questions/164496

  •  03-07-2019
  •  | 
  •  

문제

나는 스레드-안전 싱글 톤 패턴에 대해 읽고있다 :

http://en.wikipedia.org/wiki/singleton_pattern#c.2b.2b_.28using_pthreads.29

그리고 맨 아래에서 유일한 안전한 방법은 Windows에서 사용할 수없는 pthread_once를 사용하는 것입니다.

그게 스레드 안전 초기화를 보장하는 방법?

이 스레드를 읽었습니다.

C ++에서 싱글 톤의 스레드 안전 게으른 구성

원자 OS 레벨 스왑 및 비교 함수를 암시하는 것 같습니다. Windows에서 가정합니다.

http://msdn.microsoft.com/en-us/library/ms683568.aspx

이것이 내가 원하는 것을 할 수 있습니까?

편집하다: 나는 게으른 초기화를 원하고 그곳에서 수업의 한 사례 만 있으시고 싶습니다.

다른 사이트의 누군가가 네임 스페이스 내부의 글로벌을 사용하여 언급 한 사람 (그리고 싱글 톤을 반포 반으로 묘사 함)-어떻게 "반포 팬"이 될 수 있습니까?

받아 들여진 답변 :
나는 받아 들였다 조쉬의 대답 Visual Studio 2008 -NB : 미래의 독자를 위해이 컴파일러를 사용하지 않는 경우 (또는 2005) - 허용 된 답변을 사용하지 마십시오 !!

편집하다: 코드는 반환 문을 제외하고는 잘 작동합니다. 오류가 발생합니다. 오류 C2440 : 'return': 'volatile singleton *'에서 'Singleton *'으로 변환 할 수 없습니다. 반환 값을 휘발성 싱글 톤으로 수정해야합니까?

편집하다: 분명히 Const_cast <>은 휘발성 예선을 제거 할 것입니다. Josh에게 다시 한번 감사드립니다.

도움이 되었습니까?

해결책

Visual C ++ 2005/2008을 사용하고 있다면 이중 점검 잠금 패턴을 사용할 수 있습니다. "휘발성 변수는 울타리로 작동합니다". 이것은 게으른 이니셜 화 싱글 톤을 구현하는 가장 효율적인 방법입니다.

에서 MSDN 매거진 :

Singleton* GetSingleton()
{
    volatile static Singleton* pSingleton = 0;

    if (pSingleton == NULL)
    {
        EnterCriticalSection(&cs);

        if (pSingleton == NULL)
        {
            try
            {
                pSingleton = new Singleton();
            }
            catch (...)
            {
                // Something went wrong.
            }
        }

        LeaveCriticalSection(&cs);
    }

    return const_cast<Singleton*>(pSingleton);
}

싱글 톤에 액세스 할 필요가있을 때마다 GetSingleton ()에게 전화하십시오. 처음으로 호출되면 정적 포인터가 초기화됩니다. 초기화 된 후 NULL 검사는 포인터를 읽는 것만으로 잠금을 방지합니다.

하지 마라 휴대용이 아니기 때문에 모든 컴파일러에서만 사용하십시오. 표준은 이것이 어떻게 작동하는지 보장하지 않습니다. Visual C ++ 2005는 이것을 가능하게하기 위해 휘발성의 의미를 명시 적으로 추가합니다.

당신은 선언해야합니다 중요한 섹션을 초기화하십시오 코드의 다른 곳. 그러나 초기화는 저렴하므로 게으른 초기화는 일반적으로 중요하지 않습니다.

다른 팁

보장하는 간단한 방법 싱글 톤의 크로스 플랫폼 스레드 안전 초기화 응용 프로그램의 기본 스레드에서 (싱글 톤의 정적 멤버 함수로의 호출을 통해) 명시 적으로 수행하는 것입니다. ~ 전에 응용 프로그램은 다른 스레드 (또는 적어도 싱글 톤에 액세스 할 다른 스레드)를 시작합니다.

그런 다음 싱글 톤에 대한 스레드 안전한 액세스를 보장받은 다음 뮤 테스/중요 섹션으로 일반적인 방식으로 달성됩니다.

게으른 초기화 유사한 메커니즘을 사용하여 달성 할 수 있습니다. 이와 관련된 일반적인 문제는 스레드 안전성을 제공하는 데 필요한 뮤트가 종종 싱글 톤 자체에서 초기화되어 스레드 안전 문제를 MUTEX/CRISTIC 섹션의 초기화로 밀어 넣는다는 것입니다. 이 문제를 극복하는 한 가지 방법은 응용 프로그램의 기본 스레드에서 Mutex/Critical 섹션을 작성하고 초기화 한 다음 정적 멤버 기능을 통한 호출을 통해 싱글 톤으로 전달하는 것입니다. 그런 다음 싱글 톤의 헤비급 초기화는이 사전 시작화 된 뮤텍스/임계 섹션을 사용하여 스레드 안전 방식으로 발생할 수 있습니다. 예를 들어:

// A critical section guard - create on the stack to provide 
// automatic locking/unlocking even in the face of uncaught exceptions
class Guard {
    private:
        LPCRITICAL_SECTION CriticalSection;

    public:
        Guard(LPCRITICAL_SECTION CS) : CriticalSection(CS) {
            EnterCriticalSection(CriticalSection);
        }

        ~Guard() {
            LeaveCriticalSection(CriticalSection);
        }
};

// A thread-safe singleton
class Singleton {
    private:
        static Singleton* Instance;
        static CRITICAL_SECTION InitLock;
        CRITICIAL_SECTION InstanceLock;

        Singleton() {
            // Time consuming initialization here ...

            InitializeCriticalSection(&InstanceLock);
        }

        ~Singleton() {
            DeleteCriticalSection(&InstanceLock);
        }

    public:
        // Not thread-safe - to be called from the main application thread
        static void Create() {
            InitializeCriticalSection(&InitLock);
            Instance = NULL;
        }

        // Not thread-safe - to be called from the main application thread
        static void Destroy() {
            delete Instance;
            DeleteCriticalSection(&InitLock);
        }

        // Thread-safe lazy initializer
        static Singleton* GetInstance() {
            Guard(&InitLock);

            if (Instance == NULL) {
                Instance = new Singleton;
            }

            return Instance;
        }

        // Thread-safe operation
        void doThreadSafeOperation() {
            Guard(&InstanceLock);

            // Perform thread-safe operation
        }
};

그러나 싱글 톤의 사용을 모두 피해야할만한 이유가 있습니다 (때로는 때때로 그것들이 패턴 방지):

  • 그것들은 본질적으로 글로벌 변수입니다
  • 응용 프로그램의 이질적인 부분 사이에 높은 결합으로 이어질 수 있습니다.
  • 그들은 단위 테스트를 더욱 복잡하거나 불가능하게 만들 수 있습니다 (실제 싱글 톤을 가짜 구현으로 교환하기가 어렵 기 때문에).

대안은 메인 스레드에서 클래스의 단일 인스턴스를 생성하고 초기화하여 필요한 객체로 전달하는 '논리적 싱글 톤'을 사용하는 것입니다. 이 접근법은 싱글 톤으로 만들려는 많은 객체가있는 곳에서 다루기 어려울 수 있습니다. 이 경우, 이질적인 물체는 단일 '컨텍스트'객체로 번들로 묶을 수 있으며 필요한 경우 주위를 통과 할 수 있습니다.

허용 된 솔루션을 좋아하지만 방금 또 다른 유망한 리드를 발견했으며 여기에서 공유해야한다고 생각했습니다. 일회성 초기화 (Windows)

MUTEX 또는 CRITICAL SECTION과 같은 OS 원시를 사용하여 스레드 안전 초기화를 보장 할 수 있지만 싱글 톤 포인터에 액세스 할 때마다 (잠금을 획득하기 때문에) 오버 헤드가 발생합니다. 또한 휴대용이 없습니다.

이 질문에 대해 고려해야 할 명확한 점이 하나 있습니다. 필요합니까 ...

  1. 수업의 하나의 인스턴스는 실제로 만들어졌습니다.
  2. 클래스의 많은 인스턴스가 만들어 질 수 있지만 클래스의 진정한 결정적인 사례는 하나만 있어야합니다.

웹에는 C ++에서 이러한 패턴을 구현하기위한 많은 샘플이 있습니다. 여기에 있습니다 코드 프로젝트 샘플

다음은 C#에서 수행하는 방법을 설명하지만 동일한 개념은 싱글 톤 패턴을 지원하는 모든 프로그래밍 언어에 적용됩니다.

http://www.yoda.arachsys.com/csharp/singleton.html

당신이 결정해야 할 것은 게으른 초기화를 원하시는 겁쟁이입니다. 게으른 초기화는 싱글 톤 내부에 포함 된 객체가 첫 번째 호출에서 생성되었음을 의미합니다.

MySingleton::getInstance()->doWork();

그 호출이 나중에까지 이루어지지 않으면 기사에서 설명한대로 스레드 사이에 인종 조건의 위험이 있습니다. 그러나 당신이 넣으면

MySingleton::getInstance()->initSingleton();

코드가 시작될 때 스레드 안전하다고 가정하면 더 이상 게으른 초기화가 아니므로 응용 프로그램이 시작될 때 "일부"더 많은 처리 능력이 필요합니다. 그러나 인종 조건에 대해 많은 두통이 해결 될 것입니다.

보다 휴대용적이고 쉬운 솔루션을 찾고 있다면 향상 될 수 있습니다.

부스트 :: call_once 스레드 안전 초기화에 사용할 수 있습니다.

사용하기가 매우 간단하며 다음 C ++ 0X 표준의 일부가 될 것입니다.

이 질문은 싱글 톤이 게으른 건축 여부를 요구하지 않습니다. 많은 답변이 있다고 가정하기 때문에 첫 번째 문구에 대해 다음과 같이 가정합니다.

언어 자체가 스레드 인식이 아니며 최적화 기술과 함께 휴대용 신뢰할 수있는 C ++ 싱글 톤을 작성하는 것이 매우 어렵습니다 (불가능하지는 않지만). "C ++ 및 이중 체크 고정의 위험"Scott Meyers와 Andrei Alexandrescu에 의해.

Criticsection을 사용하여 Windows 플랫폼에서 객체를 동기화하기위한 많은 답변을 보았지만 Criticatsection은 모든 스레드가 하나의 단일 프로세서에서 실행될 때만 스레드 안전입니다. 오늘은 사실이 아닐 수도 있습니다.

MSDN 인용 : "단일 프로세스의 스레드는 상호 배제 동기화를 위해 중요한 섹션 객체를 사용할 수 있습니다."

그리고 http://msdn.microsoft.com/en-us/library/windows/desktop/ms682530(v=vs.85).aspx

더 자세히 설명하십시오.

임계 섹션 객체는 단일 프로세스의 스레드에서만 중요한 섹션을 사용할 수 있다는 점을 제외하고는 MUTEX 객체에 의해 제공되는 것과 유사한 동기화를 제공합니다.

이제 "Lazy-Constructed"가 요구되는 경우 다음 솔루션은 크로스 모듈 안전 및 스레드 안전 및 휴대용입니다.

struct X { };

X * get_X_Instance()
{
    static X x;
    return &x;
}
extern int X_singleton_helper = (get_X_instance(), 1);

파일/네임 스페이스 스코핑 된 글로벌 객체 대신 로컬로 스코핑 된 정적 객체를 사용하기 때문에 크로스 모듈 안전입니다.

x_singleton_helper는 메인 또는 dllmain을 입력하기 전에 올바른 값에 할당되어야하기 때문에 스레드 안전입니다.

컴파일러가 최적화를 방지하기 위해 여기에서 "extern"을 명시 적으로 사용하고 (Scott Meyers Article, The Big Enemy is Optimizer에 대한 우려) 및 PC 린트와 같은 정적 분석 도구를 유지하십시오. "Main/dllmain 이전"은 "Expective C ++ 3rd"항목 4에서 "Single-Shreaded Startup Part"라고 불리는 Scott Meyer가 있습니다.

그러나 컴파일러가 언어 표준에 따라 get_x_instance ()를 호출 할 수 있는지 확실하지 않습니다.

Windows에서 스레드 안전 싱글 톤* 초기화를 수행하는 방법에는 여러 가지가 있습니다. 실제로 그들 중 일부는 심지어 크로스 플랫폼입니다. 당신이 링크 한 SO 스레드에서, 그들은 C로 게으르게 구성된 싱글 톤을 찾고 있었는데, 이는 조금 더 구체적이며 아래에서 작업하는 메모리 모델의 복잡한 점을 감안할 때 조금 더 까다로울 수 있습니다. .

  • 절대 사용해서는 안됩니다
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top