C++C ++ 11 메모리 모델


비고

적어도 하나의 연산이 수정 ( 스토어 연산 이라고도 함)되는 경우 동일한 메모리 위치에 액세스하려는 다른 스레드가 데이터 경쟁에 참여합니다. 이러한 데이터 경쟁으로 인해 정의되지 않은 동작이 발생 합니다. 이를 피하기 위해 이러한 스레드가 충돌하는 작업을 동시에 실행하지 못하도록해야합니다.

동기화 프리미티브 (뮤텍스, 크리티컬 섹션 등)는 이러한 액세스를 보호 할 수 있습니다. C ++ 11에 소개 된 메모리 모델은 다중 스레드 환경에서 메모리에 대한 액세스를 원자 적 연산과 펜스와 동기화하는 두 가지 새로운 방법을 정의합니다.

원자력 운영

원자 적로드원자 저장소 작업을 사용하여 지정된 메모리 위치를 읽고 쓸 수 있습니다. 편의상 이것들은 std::atomic<t> 템플릿 클래스에 싸여 있습니다. 이 클래스는 t 유형의 값을 래핑하지만 이번에는 객체에 대한 로드저장 은 원 자성입니다.

템플릿은 모든 유형에 사용할 수있는 것은 아닙니다. 사용할 수있는 유형은 구현에 따라 다르지만 일반적으로 대부분의 (또는 전체) 사용 가능한 정수 유형과 포인터 유형이 포함됩니다. 따라서 std::atomic<std::pair<bool,char>> 아마도 사용되지 않을 것이지만 std::atomic<unsigned>std::atomic<std::vector<foo> *> 를 사용할 수 있어야합니다.

원자 연산의 속성은 다음과 같습니다.

  • 정의되지 않은 동작을 발생시키지 않으면 서 모든 원자 연산을 여러 스레드에서 동시에 수행 할 수 있습니다.
  • 원자 적재 는 원자 적 객체가 생성 된 초기 값 또는 원자 적 저장 연산을 통해 그 값에 기록 된 값을 보게 될 것이다.
  • 동일한 원자 객체에 대한 원자 저장소 는 모든 스레드에서 동일하게 정렬됩니다. 쓰레드가 어떤 원자 저장소 연산의 값을 이미 본다면, 이후의 원자 적재 연산은 동일한 값 또는 후속 원자 저장소 연산에 의해 저장된 값을 보게됩니다.
  • 원자 읽기 - 수정 - 쓰기 작업은 원자로드원자 저장소 사이에 다른 원자를 저장하지 않고 일어날 수 있습니다. 예를 들어, 하나는 여러 스레드에서 카운터를 원자 적으로 증가시킬 수 있으며 스레드 간의 충돌에 관계없이 증가분은 손실되지 않습니다.
  • 원자 연산은 다른 메모리 위치와 관련하여 연산이 갖는 추가 속성을 정의하는 선택적 std::memory_order 매개 변수를받습니다.
std :: memory_order 의미
std::memory_order_relaxed 추가 제한 없음
std::memory_order_releasestd::memory_order_acquire 경우 load-acquire 저장 값을보고 store-release전에 순서가 저장 store-release 부하 획득 한 후 염기 서열을로드되기 전에 발생
std::memory_order_consume memory_order_acquire 와 유사하지만 종속로드에 대해서만
std::memory_order_acq_rel load-acquirestore-release
std::memory_order_seq_cst 순차적 일관성

이러한 메모리 순서 태그는 세 가지 다른 메모리 순서 지정 분야를 허용합니다. 즉, 순차적 일관성 , 완화 및 형제와 함께 릴리스 획득릴리스를 사용 합니다.

순차 일관성

원자 조작에 대해 메모리 순서가 지정되지 않은 경우, 순서는 순차 일관성을 기본값 으로 합니다. 이 모드는 std::memory_order_seq_cst 작업 태그를 지정하여 명시 적으로 선택할 수도 있습니다.

이 순서로 메모리 조작은 원자 조작을 넘을 수 없습니다. 모든 메모리 연산은 원자 연산과 원자 연산이 발생하기 전에 원자 연산이 발생하기 전에 일어난다. 이 모드는 아마도 가장 쉬운 방법 일 수 있지만 성능에 가장 큰 불이익을 초래합니다. 또한 원자 연산을 지나서 연산을 재정렬하려고 시도하는 모든 컴파일러 최적화를 방지합니다.

편안한 주문

순차적 일관성 의 반대는 편안한 메모리 정렬입니다. std::memory_order_relaxed 태그로 선택됩니다. Relaxed atomic 연산은 다른 메모리 연산에 대한 제한을 부과하지 않는다. 여전히 남아있는 유일한 효과는 조작 자체가 여전히 원자 적이라는 것입니다.

주문 취소 주문

원자 저장소 연산에는 std::memory_order_release 태그를 지정할 수 있으며 원자 std::memory_order_release 작업에는 std::memory_order_acquire 태그를 지정할 수 있습니다. 첫 x 째 조작은 (원자) 저장 해제 라고하며 두 x 째 조작은 (원자) load-acquire라고 합니다.

load-acquire상점 릴리스에 의해 쓰여진 값을 보게되면 다음과 같은 결과가 발생합니다. 상점 릴리스로드 획득 전에 순서화 된로드 조작에 대해 가시적으로되기 전에 모든 저장 조작이 순서화됩니다.

원자 읽기 - 수정 - 쓰기 연산은 또한 누적 태그 std::memory_order_acq_rel 받을 수있다. 이렇게하면 작업의 원자 적로드 부분이 원자 적재량을 획득하게 되고 원자 저장량 부분은 원자력 저장 방출이 됩니다.

컴파일러는 원자 상점 릴리스 조작 후에 상점 조작을 이동할 수 없습니다. 또한 원자 적로드 획득 (또는 로드 소비 ) 전에로드 조작을 이동하는 것이 허용되지 않습니다.

또한 원자 적재 - 방출 또는 원자 축적 - 획득 이 없음에 유의하십시오. 이러한 작업을 만들려고하면 작업이 느슨해 집니다.

릴리즈 - 소비 주문

이러한 결합 해제-획득을 유사하지만,이 시간은 원자 부하가 태그되는 std::memory_order_consume(원자) 부하 소비 동작된다. 이 모드는 서열화 후에로드 동작 중 이들에 의해로드 된 값에 따라 부하만을 소비하는 것이 유일한 차이점 - 취득 떼면 동일한 부하 소비하는 정렬된다.

울타리

펜스는 또한 스레드간에 메모리 연산을 정렬 할 수있게합니다. 울타리는 릴리스 울타리 또는 취득 울타리 중 하나입니다.

획득 펜스가 획득 펜스보다 먼저 발생하면 펜스가 획득 펜스 이후에 시퀀싱 된로드에 표시되기 전에 시퀀스가 ​​저장됩니다. 획득 울타리 이전에 릴리스 펜스가 발생하도록 보장하기 위해 완화 된 원자 연산을 비롯한 다른 동기화 프리미티브를 사용할 수 있습니다.

C ++ 11 메모리 모델 관련 예