문제

는 방법이 있을 구현하는 단일 객체는 C++에서는:

  1. 지연 구성 스레드에 안전한 방법으로(두 개의 스레드 수도 있습을 동시에 첫 번째 사용자의 싱글턴-그것은 여전히 건설 될 후).
  2. 에 의존하지 않는 정적 변수를 생성되는 사전에(그래서 단일 객체가 자체적 안전을 사용하의 건설 기간 동안 정적 변수).

(나는 몰라 내 C++충분히지만,그것은의 경우는 필수하고 일정한 정적 변수 초기화 전에 모든 코드를 실행(즉,전에,정적 생성자를 실행-자신의 값을 수 있는"초기화 프로그램에서"이미지)?그렇다면-아마도 이러에 악용될 수 있습을 구현하는 단일 mutex-는할 수 있습을 보호하는 데 사용되는 창조의 실제 단일..)


한,그것은 보인다는 몇 가지 좋은 이제(는 수 mark2 또는 3 로 응답).가 나타납니다 두 가지 광범위한 솔루션:

  1. 사용 정체되는 초기화(반대하여 동적 초기화)의 포드는 정적 변수,그리고 구현하는 내 자신의 mutex 으로는 사용하는 기본 제공 원자다.이 유형의 솔루션을 내가 암시에 나의 질문,그리고 나는 이미 알고 있었.
  2. 사용 몇 가지 다른 도서관과 같은 기능 pthread_once부스트::call_once.이러한 확실히 알지 못했고 감사에 대한 답변 게시됩니다.
도움이 되었습니까?

해결책

기본적으로,당신은 요청에 대한 동기화된 창의 단일을 사용하지 않고,어떤 동기화(이전에 건설된 변수).일반적으로,아,이것은 가능하지 않습니다.당신이 뭔가를 필요로 사용할 수 있는 동기화를 위해.

로에 대한 다른 질문에 그렇다,정적될 수 있는 변수에 정적으로 초기화(i.e아 런타임이 필요한 코드)이 보장됩니다 초기화 전에 다른 코드가 실행됩니다.이 사용하는 것을 가능하게 합니다 정적으로 초기화 mutex 를 동기화하는 창조의 단일.

에서 2003 년 개정판의 C++표준:

체와 정체되 저장 기간(3.7.1)shall be zero-초기화(8.5)하기 전에 다른 모든 초기화를 수행하는 장소입니다.제로 초기화와 초기화 함께 일정한 표정은 집단적으로 정적 초기화다른 모든 초기화를 동적 초기 설정입니다.체의 포드 유형(3.9)정적으로 저장 기간이 초기화된 지속적인 표현(5.19)될 것 초기화 전에 모든 초기화를 수행하는 동적 장소입니다.체와 함께 정관에서 정한 기간 네임스페이스의 범위에서 같은 번역 유닛과 동적으로 초기화하여 초기화되는 순서대로의 정의에 나타나 번역 단위입니다.

는 경우 당신이 사용하는 것입니다 단일을 초기화하는 동안 다른 정적 개체로,나는 당신을 찾을 수 있는 동기화가 아닌 문제입니다.을 내가 아는 모든 주요 컴파일러를 초기화 정적 객체에서 하나의 스레드,그래서 스레드에 안전하는 동안 정적 초기화.를 선언할 수 있습니다 당신의 단일 포인터가 NULL 이 될 수 있음을 확인 여부를 확인하려면 초기화되기 전에 당신은 그것을 사용합니다.

그러나,이것은 당신 는 데 사용할 것이 단일 동안 정적 초기화.이것은 또한에 의해 보장 표준,그래서 당신이 원하는 것을 완전히 안전한 사용을 정적으로 초기화 mutex.

편집:크리스의 제안을 사용 atomic compare-and-swap 것이 확실하게 작동합니다.이식하지 않아도 되는 경우에는 문제점(와 추가로 만드는 임시 싱글이 문제가되지 않음),그것은 약간 낮은 오버헤드 솔루션입니다.

다른 팁

불행하게도,매트의 기능에 대답은 무엇이라고 더블 잠금 확인 는 지원되지 않 C/C++메모리 모델이다.(그에 의해 지원되는 Java1.5 이상—고 나는 생각한다.NET—메모리 모델이다.) 이것이 의미하는 사이에 시간 pObj == NULL 체크인 장소 및 잠금(mutex)인수 pObj 이미 되었을 수도 있습니다 할당된 다른 스레드가 있습니다.스레드로 전환될 때마다 발생 OS 그것을 원하지 않는 사이에"라인"프로그램(는 의미가 없 게시 컴파일하는 대부분의 언어에서).

또한,매트 인,그가 사용하는 int 으로 자물쇠가 아닌 OS 원시적인 것입니다.그렇게 하지 않습니다.적절한 잠금 장치의 사용을 필요로 메모리 방벽한 지침,잠재적으로 캐시 온라인 플러시,그리고;사용하는 운영체제 시스템의 기본 요소에 대한 잠그고 있습니다.이것은 특히 중요하기 때문에 기본 형식을 사용할 수 있는 변경 사이의 개별 CPU 라인 운영 시스템에서 실행됩;어떤 작품에서는 CPU 푸에서 작동하지 않을 수 있습니다 CPU Foo2.대부분의 운영체제 중 하나 기본적으로 지원 POSIX threads(인수를 입력하)또는 제안들에 대한 래퍼 OS 레딩 패키지,그래서 그것은 종종 가장 좋을 설명하는 예로 그들을 사용하고 있습니다.

는 경우에 당신의 운영 시스템 제공하는 적절한 기본 형식,그리고 절대적으로 필요한 경우 그 성능을 위해 일을 대신 이의 유형 잠금/초기화를 사용할 수 있습니다 원자 비교하고 교환 운영를 초기화하는 글로벌 공유 변수입니다.기본적으로,당신이 무엇을 쓰는 다음과 같습니다.

MySingleton *MySingleton::GetSingleton() {
    if (pObj == NULL) {
        // create a temporary instance of the singleton
        MySingleton *temp = new MySingleton();
        if (OSAtomicCompareAndSwapPtrBarrier(NULL, temp, &pObj) == false) {
            // if the swap didn't take place, delete the temporary instance
            delete temp;
        }
    }

    return pObj;
}

이 경우에만 작동이 안전하는 여러 인스턴스를 만들의 싱글(하나의 스레드당에 일어나는 호출 GetSingleton()동시에)및 그 엑스트라습니다.이 OSAtomicCompareAndSwapPtrBarrier 기능 제공 Mac OS X—대부분의 운영을 시스템을 제공합와 유사한 기본지 여부 확인 pObjNULL 만 실제로 설정 temp 하는 경우 그것입니다.이 하드웨어를 사용하는 지를 정말로,그대로 수행 교환 고 말하지 그런 일이 일어났습니다.

다른 시설을 활용하면 OS 를 제공는 이 두 극단 사이에서 이 pthread_once.이 설정할 수 있습 기능이 실행되는 유일 한 번에 의해 기본적으로의 모든 일을 잠그기/방벽/등이 있습니다.속임수 위해 당신에 상관없이는 방법을 여러 번 호출하는 방법에는 많은 스레드의 호출됩니다.

여기에 아주 간단한 느리게 건설 단일 getter:

Singleton *Singleton::self() {
    static Singleton instance;
    return &instance;
}

이 게으른하고,다음 C++표준(C++0x)그것을 요구하는 스레드에 안전합니다.사실 내가 믿는 이상은 g++를 구현하이 스레드에 안전하게 보관하시기 바랍니다.그래서 만약 당신의 대상 컴파일러 를 사용하는 경우 컴파일러를 구현하이 스레드에 안전한 방법(아마도 새로운 Visual Studio 컴파일러가?모르겠어),이 될 수 있습니다.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2513.html 이 주제입니다.

당신은 할 수 없는것 없이 정체되는 변수,그러나 당신은 기꺼이 하나를 허용할 수 있도록 사용할 수 있습니다 습니다.스레드 이 목적을 위해.읽고"한 시간이 초기화"섹션에 대한 더 많은 정보.

그런 다음에서 단일 접근 기능,사용 boost::call_once 를 구성하는 개체를 반환합니다.

을 위한 gcc,이것은 오히려 쉽습니다:

LazyType* GetMyLazyGlobal() {
    static const LazyType* instance = new LazyType();
    return instance;
}

GCC 것입니다 있는지 확인 초기화를 유념해야 합니다. VC++,이하지 않은 경우. :-(

하나의 중요한 문제와 이 메커니즘의 부족이 테스트 기능:필요할 경우를 재설정 LazyType 새로운 하나의 테스트,또는 변경하고 싶 LazyType*을 MockLazyType*,할 수 없습니다.이 주어진,그것은 일반적으로 최고 사용하는 정적 mutex+정적 포인터이다.

또한,아마도 따:그것은 최고의하지 않는 것이 항상 정적 비 POD 형식입니다.(포인터를 깍은 확인됩니다.) 그 이유는 많다:당신이 언급한,초기화 순서를 정의되지 않습--도 순서를 소멸자라고 하지만입니다.이 때문에,프로그램이 끝나면 그들은 나가십시오;자주 큰 문제가되지 않습니다,하지만 때로는 showstopper 때 프로파일러를 사용하려고 필요 깨끗하고 종료됩니다.

이 질문에 대한 답이 있는지,내 생각은 거기에 몇 가지 다른 점을 언급합니다:

  • 하려는 경우 지연의 인스턴스화 단일을 사용하는 동안 포인터를 동적으로 할당 인스턴스에,당신은 당신을 확인하는 청소 오른쪽 지점에서.
  • 당신이 사용할 수 있 매트의 솔루션,하지만 당신은 것을 사용할 필요가 적절한 mutex/중 섹션에 대한 잠금을 확인하여"pObj==NULL"전후 잠급니다.물론, pObj 또한 것야 static ;) .Mutex 것은 불필요하게 무거운 이 경우에,당신은 더 나을 것으로 중요한 섹션입니다.

그러나 이미 말할 수 없습니다 보면 게으른 초기화를 사용하지 않고 하나 이상의 동기화 기울입니다.

편집:Yup 데릭,당신이 맞습니다.내 나쁘다.:)

당신이 사용할 수 있 매트의 솔루션,하지만 당신은 것을 사용할 필요가 적절한 mutex/중 섹션에 대한 잠금을 확인하여"pObj==NULL"전후 잠급니다.물론,pObj 도 해야되고,).Mutex 것은 불필요하게 무거운 이 경우에,당신은 더 나을 것으로 중요한 섹션입니다.

OJ,작동하지 않습니다.크리스 지적,즉시 확인 잠그는 작동하지 않을 현재에서는 C++표준입니다.보: C++고 위험의 더블 잠금 확인

편집:문제 없습니다,OJ.그것은 정말 좋은 언어로 작동지 않습니다.나는 그것을 기대에서 작동하는 C++0x(지만 나의 특정)기 때문에,그와 같은 편리한 사용합니다.

  1. 에 읽 약한 메모리 모델이다.그것은 두 번 확인 및 자물쇠 을 갖.인텔은 강력한 메모리 모델은(아직),그래서 인텔의

  2. 신중하게 사용하"휘발성"을 피하는 캐싱하는 부품의 개체 레지스터에,그렇지 않으면 당신이 초기화 개체 포인터,하지만 개체고 다른 스레드가 충돌

  3. 의 순서를 정적 변수 초기화 대 공유 코드 로드가 때때로 사소한되지 않습니다.본 경우는 코드를 소멸 개체에 이미 언로드므로,이 프로그램에 종료

  4. 이러한 객체는 파괴 하드 제대로

일반 싱글이드 바 및 하드를 디버그.그것의 더 나은 그들을 피하기 위해합니다.

내 생각을 말하지 않기 때문에 이렇게 안전하지 않고는 아마 휴식보다 더 자주 다 초기화 이 물건에 main() 하지 않는 인기가 있습니다.

(그래,내가 알고있는 제안하신 것을 하려고 해서는 안됩지 흥미로운 물건에서 생성자의 글로벌다.는 점이다.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top