에 대한 여러 기사, 대화 및 스택 오버플로 질문을 듣고 읽었으며 std::atomic
그것을 잘 이해하고 싶습니다. MESI (또는 파생) 캐시 일관성 프로토콜, 버퍼 저장, 대기열 무효화 등의 가능한 지연으로 인해 캐시 라인 쓰기 가시성과 약간 혼동되기 때문에.
x86은 더 강력한 메모리 모델을 가지고 있으며 캐시 무효화가 지연되면 x86이 시작된 작업을 되돌릴 수 있음을 읽었습니다. 그러나 이제는 플랫폼과 독립적으로 C ++ 프로그래머로 가정 해야하는 것에 대해서만 관심이 있습니다.
[T1 : 스레드 1 T2 : 스레드 2 V1 : 공유 원자 변수]
나는 std :: atomic이 그것을 보증한다는 것을 이해합니다.
(1) 변수에서 데이터 레이스가 발생하지 않습니다 (캐시 라인에 독점적으로 액세스 할 수 있기 때문에).
(2) 우리가 사용하는 memory_order에 따라 (방벽 이전, 장벽 후 또는 둘 다) 순차적 일관성이 발생한다는 것을 보장합니다 (장벽 포함).
(3) T1의 원자 쓰기 (V1) 후에 T2의 원자 RMW (V1)가 일관됩니다 (캐시 라인이 T1의 기록 된 값으로 업데이트 됨).
그러나 캐시 일관성 입문서에서 언급했듯이
이러한 모든 것의 의미는 기본적으로로드가 오래된 데이터를 가져올 수 있다는 것입니다 (해당 무효화 요청이 무효화 대기열에있는 경우).
따라서 다음이 맞습니까?
(4) std::atomic
는 T2가 T1의 원자 쓰기 (V) 후에 원자 판독 (V)에서 '스톨'값을 읽지 않는다고 보장하지 않습니다.
(4)가 맞는지에 대한 질문 : T1의 원자 쓰기가 지연과 상관없이 캐시 라인을 무효화하는 경우 원자 RMW가 작동하지만 원자 판독이 아닌 경우 무효화가 유효하기를 기다리는 이유는 무엇입니까?
(4)가 틀린 경우 질문 : 스레드가 언제 실행에서 'stale'값을 읽을 수 있고 "보일 수 있습니까"?
나는 당신의 답변을 많이 주셔서 감사합니다
업데이트 1
그래서 내가 (3)에 잘못 된 것 같습니다. 초기 V1 = 0에 대해 다음 인터리브를 상상해보십시오.
T1: W(1)
T2: R(0) M(++) W(1)
이 경우 T2의 RMW가 W (1) 이후에 완전히 발생한다고 보장되지만 여전히 'stale'값을 읽을 수 있습니다 (잘못된). 이것에 따르면 atomic은 전체 캐시 일관성을 보장하지 않으며 순차적 일관성 만 보장합니다.
업데이트 2
(5) 이제이 예제를 상상해보십시오 (x = y = 0이고 원자 적입니다).
T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");
우리가 이야기 한 바에 따르면, 화면에 "msg"가 표시되는 것은 T1 이후에 T2가 실행 된 것 이상의 정보를 제공하지 않습니다. 따라서 다음 중 하나가 실행되었을 수 있습니다.
- T1 <T3 <T2
- T1 <T2 <T3 (여기서 T3은 x = 1이지만 y = 1은 아님)
맞습니까?
(6) 스레드가 항상 'stale'값을 읽을 수있는 경우 일반적인 "게시"시나리오를 취했지만 일부 데이터가 준비되었다는 신호를 보내는 대신 반대로 데이터를 삭제하면 어떻게됩니까?
T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr->doSomething();
여기서 T2는 is_enabled가 false임을 확인할 때까지 삭제 된 ptr을 계속 사용합니다.
(7) 또한 스레드가 'stale'값을 읽을 수 있다는 사실은 뮤텍스 를 하나의 잠금없는 원자 권리로 구현할 수 없다는 것을 의미 합니까? 스레드 간 동기화 메커니즘이 필요합니다. 잠금 가능한 원자가 필요합니까?