2

그래서 우리는 double-checked-locking이 C++에서 작동하지 않는다는 것을 알고 있습니다. 최소한 이식성있는 방식은 아닙니다.트리플 체크 잠금?

저는 지형 광선 추적기에 사용하는 게으른 쿼드 트리에서 깨지기 쉬운 구현을 실현했습니다. 그래서 저는 메모리 사용량을 4 배로 늘리거나 구현 된 알고리즘의 상당 부분을 다시 정렬하는 것을 좋아하지 않으므로 안전한 방식으로 게으른 초기화를 사용하는 방법을 찾으려고했습니다.

이 통과가 C++ and the Perils of Double-Checked Locking의 12 페이지의 패턴에서 영감을하지만, 그것을 어떻게하려고한다 저렴 :

#pragma flush 또한 컴파일러와 프로세서는 '원 하드 시퀀스 지점 역할을한다고 가정
(pseudo code!) 

struct Foo { 
    bool childCreated[4]; 
    Mutex mutex[4]; 
    Foo child[4]; 

    void traverse (...) { 
     ... 
     if (!childCreated[c]) { 
      // get updated view 
      #pragma flush childCreated[c] 
      if (!childCreated[c]) { 
       ScopedLock sl (mutex[c]); 
       if (!childCreated[c]) { 
        create (c); 
        #pragma flush childCreated[c] 
        childCreated[c] = true; 
       } 
      } 
     } 
    } 
} 

그 (것)들의 맞은 편에 오퍼레이션을 재 순서화 할 수있다.

어떤 문제가 있습니까?

편집 : 버전 2, (제 3 플러시를 소개합니다) 계정 Vlads에 답을하려고 :

(pseudo code!) 

struct Foo { 
    bool childCreated[4]; 
    Mutex mutex[4]; 
    Foo child[4]; 

    void traverse (...) { 
     ... 
     if (!childCreated[c]) { 
      // get updated view 
      #pragma flush childCreated[c] 
      if (!childCreated[c]) { 
       ScopedLock sl (mutex[c]); 
       #pragma flush childCreated[c] 
       if (!childCreated[c]) { 
        create (c); 
        #pragma flush childCreated[c] 
        childCreated[c] = true; 
       } 
      } 
     } 
    } 
} 

편집 : 버전 3은, 어떻게 든 버전 2이 꽤 상당 찾을 I 때문에 아이를 사용하지 않고 유효성을 검사하기위한 기본 플래그입니다. 기본적으로 아이를 생성하고 플래그를 쓰는 것 사이의 메모리 장벽에 의존합니다.

(pseudo code!) 

struct Foo { 
    bool childCreated[4]; 
    Mutex mutex[4]; 
    Foo child[4]; 

    void traverse (...) { 
     ... 
     if (!childCreated[c]) { 
      ScopedLock sl (mutex[c]); 
      #pragma flush childCreated[c] 
      if (!childCreated[c]) { 
       create (c); 
       #pragma flush childCreated[c] 
       childCreated[c] = true; 
      } 
     } 
    } 
} 

답변

3

패턴이 올바르지 않은 것으로 보입니다. 스레드 # 1이 첫 번째 #pragma flush 다음까지 실행되는 경우를 생각해보십시오. 그러면 제어가 스레드 # 2로 전환되어 계속 진행되고 c이 생성되고 제어는 두 번째 #pragma flush 바로 전에 수행됩니다. 이제 첫 번째 스레드가 깨어 나고 자식이 새로 생성됩니다.

편집 : 죄송합니다. 잘못하면 잠금을 사용할 수 없습니다.

편집 2 : 아니요, 값이 스레드 # 1에서 플러시되지 않기 때문에 여전히 맞습니다.

+0

다른 개정판을 추가했습니다. 비록 내가 그 날이 길다는 것을 인정해야 하겠지만, 나는 천천히 두뇌에서 빠져 나간다. –

+0

우리는 여전히'create (c);'와'childCreated [c] = true;'사이에 장벽이 필요하다고 생각합니다. 그렇지 않으면 다시 정렬 될 수 있습니다. (thread # 3은 아직 생성되지 않은'c'를 사용하여 시작할 수 있습니다) . – Vlad

+0

다시 두 번 확인한 패턴 인 세 번째 개정판을 추가 할 때, 나는 정확히 깨달았습니다. 그러나 세 번째 개정판을 보았을 때, 그것은 너무나 사소한 것처럼 보입니다. 더 많은 커피를 마시는 것은 이미 포화 된 상태이므로 더 이상 선택할 수 없습니다./ –