C ++ std :: memory_order_relaxed 혼란
나는 GCC Wiki에서 C ++ 메모리 장벽에 대한 이 기사 를 읽고 있었다 .
이 시점까지 매우 간단했습니다.
반대 접근 방식은 std :: memory_order_relaxed입니다. 이 모델은 전 발생 제한을 제거하여 훨씬 적은 동기화를 허용합니다. 이러한 유형의 원자 적 작업은 데드 스토어 제거 및 공통화와 같은 다양한 최적화를 수행 할 수도 있습니다. 따라서 이전 예에서 :
-Thread 1-
y.store (20, memory_order_relaxed)
x.store (10, memory_order_relaxed)
-Thread 2-
if (x.load (memory_order_relaxed) == 10)
{
assert (y.load(memory_order_relaxed) == 20) /* assert A */
y.store (10, memory_order_relaxed)
}
-Thread 3-
if (y.load (memory_order_relaxed) == 10)
assert (x.load(memory_order_relaxed) == 10) /* assert B */
스레드는 시스템 전체에서 동기화 될 필요가 없기 때문에이 예제의 어설 션은 실제로 실패 할 수 있습니다.
자, 이것도 간단합니다. 계속 진행하겠습니다.
-Thread 1-
x.store (1, memory_order_relaxed)
x.store (2, memory_order_relaxed)
-Thread 2-
y = x.load (memory_order_relaxed)
z = x.load (memory_order_relaxed)
assert (y <= z)
어설 션은 실패 할 수 없습니다. 2의 저장소가 스레드 2에 표시되면 더 이상 값 1을 볼 수 없습니다. 이렇게하면 별칭이있을 수있는 다른 참조의 완화 된로드에서 한 변수의 완화 된로드가 병합되는 것을 방지합니다.
이것이 나를 혼란스럽게하는 이유입니다. 순서가 스레드 1에서 동기화되지 않았기 때문에 y가 값 2를로드 할 수없고 z가 값 1을로드하고 어설 션이 실패하도록하는 이유는 무엇입니까?
답변
Relaxed ordering은 relax-modified 원자에 대한 순서가 아니라 다른 메모리 액세스 와 관련된 작업 순서에 상대적입니다. 첫 번째 경우에서 10을 볼 수 있다는 사실은 x의 값과 관련하여 아무 의미가 없습니다 y. 그 반대.
그러나 두 번째 경우는 동일한 원자 객체에 영향을 미치기 때문에 다릅니다.
[intro.races] / 10 은 스레드 내에서 한 작업이 다른 작업보다 먼저 시퀀싱되면 해당 작업이 다른 작업보다 "앞서 발생"한다고 알려줍니다. 그리고 [intro.races] / 14-17은 원자와 관련하여 다음과 같은 동작을 설명합니다 .
앞의 네 가지 일관성 요구 사항은 두 작업이 모두 부하가 완화 된 경우에도 컴파일러에서 원자 적 작업을 단일 객체로 재정렬하는 것을 효과적으로 허용하지 않습니다.
그리고 그것이 여러분이 여기있는 것입니다. 모든 수정은 동일한 개체에서 발생하므로 어떤 순서 로든 발생해야합니다 . 그 순서를 정확히 결정할 수 없더라도 순서는 코드의 "이전에 발생하는"관계를 존중해야합니다.
스레드 1의 두 작업은 "이전 발생"관계에 따라 정렬됩니다. 그리고 스레드 2의 작업은 "이전 발생"관계에 따라 자체적으로 정렬됩니다.
모두 동일한 원자 객체에서 작동하므로 y2의 값을 가져 오면 "후에 발생" x이 2로 설정되어 x있어야합니다 . 따라서 액세스 순서는 "x = 1, x = 2, x "를 읽으십시오. 그리고의 마지막 읽기 x는를 처음 읽은 후에 발생 x하므로 가져 오는 값은 1이 될 수 없습니다.