programing

동시성 : C ++ 11 메모리 모델의 원자 및 휘발성

randomtip 2021. 1. 18. 07:55
반응형

동시성 : C ++ 11 메모리 모델의 원자 및 휘발성


전역 변수는 2 개의 서로 다른 코어에서 동시에 실행되는 2 개의 스레드에서 공유됩니다. 스레드는 변수에 쓰고 읽습니다. 원자 변수의 경우 한 스레드가 오래된 값을 읽을 수 있습니까? 각 코어는 캐시에 공유 변수의 값을 가질 수 있으며 한 스레드가 캐시의 사본에 쓸 때 다른 코어의 다른 스레드는 자체 캐시에서 오래된 값을 읽을 수 있습니다. 아니면 컴파일러가 다른 캐시에서 최신 값을 읽기 위해 강력한 메모리 순서를 지정합니까? C ++ 11 표준 라이브러리에는 std :: atomic 지원이 있습니다. volatile 키워드와 어떻게 다른가요? 위의 시나리오에서 휘발성 및 원자 유형이 어떻게 다르게 작동합니까?


첫째, volatile원자 적 접근을 의미하지 않습니다. 메모리 매핑 I / O 및 신호 처리와 같은 작업을 위해 설계되었습니다. volatile와 함께 사용할 때는 완전히 불필요 std::atomic하며 플랫폼이 달리 문서화하지 않는 한 volatile스레드 간의 원자 액세스 또는 메모리 순서에 영향을 미치지 않습니다.

다음과 같이 스레드간에 공유되는 전역 변수가있는 경우 :

std::atomic<int> ai;

그런 다음 가시성 및 순서 지정 제약 조건은 작업에 사용하는 메모리 순서 지정 매개 변수와 잠금, 스레드 및 다른 원자 변수에 대한 액세스의 동기화 효과에 따라 달라집니다.

추가 동기화가없는 경우 한 스레드가 값을 쓰면 ai다른 스레드가 주어진 시간 동안 값을 볼 수 있다는 보장이 없습니다. 표준은 "적당한 시간 내에"표시되어야한다고 지정하지만 주어진 액세스는 오래된 값을 반환 할 수 있습니다.

의 기본 메모리 순서는 모든 변수에 std::memory_order_seq_cst대한 모든 std::memory_order_seq_cst작업에 대해 단일 전역 총 순서를 제공 합니다. 이것은 당신이 오래된 가치를 얻을 수 없다는 것을 의미하지는 않지만, 당신이 얻는 가치가 당신의 작업이있는 전체 순서에 따라 결정되고 결정된다는 것을 의미합니다.

두 개의 공유 변수가 x있고 y처음에는 0이고 한 스레드가에 1을 x쓰고 다른 y스레드가에 2를 쓰는 경우 둘 다 읽는 세 번째 스레드는 (0,0), (1,0), (0,2)를 볼 수 있습니다. ) 또는 (1,2) 작업 사이에 순서 지정 제약이 없기 때문에 작업이 전역 순서의 모든 순서로 나타날 수 있습니다.

두 쓰기가 모두 동일한 스레드에서 발생하는 경우 x=1이전에 수행 y=2하고 읽기 스레드가 그 y전에 읽은 읽기 스레드 x(0,2)는 더 이상 유효한 옵션이 아닙니다. 읽기 y==2는 이전 쓰기 x가 표시됨 의미하기 때문 입니다. 다른 3 개의 쌍 (0,0), (1,0) 및 (1,2)는 2 개의 읽기와 2 개의 쓰기가 인터리브되는 방식에 따라 여전히 가능합니다.

std::memory_order_relaxed또는 같은 다른 메모리 순서를 사용 std::memory_order_acquire하면 제약 조건이 더 완화되고 단일 전역 순서가 더 이상 적용되지 않습니다. 추가 동기화가없는 경우 스레드는 변수를 분리하기 위해 두 저장소의 순서에 반드시 동의 할 필요조차 없습니다.

"최신"값을 보장하는 유일한 방법은 exchange(), compare_exchange_strong()또는 같은 읽기-수정-쓰기 작업을 사용하는 것 fetch_add()입니다. 읽기-수정-쓰기 작업에는 항상 "최신"값에서 작동한다는 추가 제약 조건이 있으므로 ai.fetch_add(1)일련의 스레드에 의한 작업 시퀀스는 중복이나 간격이없는 일련의 값을 반환합니다. 추가 제약이없는 경우에도 어떤 스레드가 어떤 값을 볼 것인지 보장 할 수 없습니다.

원자 연산 작업은 복잡한 주제입니다. 많은 배경 자료를 읽고 atomics로 프로덕션 코드를 작성하기 전에 게시 된 코드를 검토하는 것이 좋습니다. 대부분의 경우 잠금을 사용하는 코드를 작성하는 것이 더 쉬우 며 현저히 덜 효율적입니다.


volatile 원자 연산은 다른 배경을 가지고 있으며 다른 의도로 도입되었습니다.

volatile이전부터 시작되며 주로 메모리 매핑 IO에 액세스 할 때 컴파일러 최적화를 방지하도록 설계되었습니다. 최신 컴파일러는에 대한 최적화를 억제하는 것 이상을 수행하지 않는 경향이 volatile있지만 일부 컴퓨터에서는 메모리 매핑 된 IO에도 충분하지 않습니다. 신호 처리기의 특별한 경우를 제외하고 setjmp, longjmpgetjmp시퀀스 (C 표준, 그리고 신호의 경우 POSIX 표준은 추가적인 보장을 제공 임), 그것은 특별한 추가 지시없이 어디 현대 컴퓨터에 쓸모없는 것으로 간주되어야 (울타리 또는 메모리 장벽) 하드웨어가 재정렬하거나 특정 액세스를 억제 할 수도 있습니다. 당신이 사용해서는 안되기 때문에setjmpet al. C ++에서 이것은 다소간 신호 처리기를 떠나며, 다중 스레드 환경에서, 적어도 Unix에서는 이들을위한 더 나은 솔루션이 있습니다. 커널 코드에서 작업하고 컴파일러가 해당 플랫폼에 필요한 모든 것을 생성하도록 할 수있는 경우 메모리 매핑 된 IO도 가능합니다. (표준에 따르면 volatile액세스는 관찰 가능한 동작이며 컴파일러는이를 준수해야합니다. 그러나 컴파일러는 "액세스"가 의미하는 것을 정의하게되며 대부분은이를 "로드 또는 저장 기계 명령이 실행되었습니다"로 정의하는 것처럼 보입니다. , 최신 프로세서에서 버스에 읽기 또는 쓰기주기가 반드시 있어야한다는 의미는 아니며 예상 한 순서보다 훨씬 적습니다.)

이러한 상황을 감안할 때 C ++ 표준은 원자 적 액세스를 추가하여 스레드 전반에 걸쳐 일정 수의 보장을 제공합니다. 특히 원자 적 액세스를 중심으로 생성 된 코드에는 하드웨어가 액세스 순서를 변경하는 것을 방지하고 액세스가 멀티 코어 머신의 코어간에 공유되는 전역 메모리로 전파되도록하는 데 필요한 추가 지침이 포함됩니다. (표준화 노력의 한 시점에서 Microsoft는 이러한 의미를에 추가 할 것을 제안 volatile했으며 일부 C ++ 컴파일러도 그렇게한다고 생각합니다. 그러나위원회에서 문제를 논의한 후 Microsoft 담당자를 포함한 일반적인 합의는 떠나는 것이 낫다volatile원래 의미를 가지고 원자 유형을 정의합니다.) 또는 코드에 필요한 모든 명령을 실행하는 뮤텍스와 같은 시스템 수준 기본 형식을 사용합니다. (그렇게해야합니다. 메모리 액세스 순서에 대한 보증없이 뮤텍스를 구현할 수 없습니다.)


다음은 두 가지가 무엇인지에 대한 기본 개요입니다.

1) Volatile 키워드 :
이 값이 언제든지 변경 될 수 있으므로 레지스터에 캐시하지 않아야 함을 컴파일러에 알립니다. C에서 이전 "register"키워드를 찾으십시오. "Volatile"은 기본적으로 "register"의 "+"에 대한 "-"연산자입니다. 최신 컴파일러는 이제 기본적으로 명시 적으로 요청하는 데 사용되는 "등록"최적화를 수행하므로 더 이상 '휘발성'만 표시됩니다. 휘발성 한정자를 사용하면 처리에서 오래된 값을 사용하지 않고 더 이상 사용하지 않습니다.

2) Atomic :
Atomic 연산은 단일 클럭 틱에서 데이터를 수정하므로 업데이트 도중에 다른 스레드가 데이터에 액세스 할 수 없습니다. 일반적으로 하드웨어가 지원하는 단일 클럭 어셈블리 명령으로 제한됩니다. ++,-, 2 개의 포인터 교체 등. 이것은 ORDER에 대해 아무 것도 말하지 않는다는 점에 유의하십시오. 서로 다른 스레드가 원자 적 명령을 실행할 것이며, 병렬로 실행되지 않을 것입니다. 그래서 주문을 강요 할 수있는 모든 추가 옵션이 있습니다.


Volatile과 Atomic은 다른 용도로 사용됩니다.

Volatile : 최적화를 피하기 위해 컴파일러에 알립니다. 이 키워드는 예기치 않게 변경되는 변수에 사용됩니다. 따라서 하드웨어 상태 레지스터, ISR의 변수, 다중 스레드 응용 프로그램에서 공유되는 변수를 나타내는 데 사용할 수 있습니다.

Atomic : 다중 스레드 응용 프로그램의 경우에도 사용됩니다. 그러나 이렇게하면 다중 스레드 응용 프로그램에서 사용하는 동안 잠금 / 중단이 발생하지 않습니다. 원자 작전은 인종이없고 나눌 수 없습니다. 사용의 주요 시나리오 중 일부는 잠금이 사용 가능한지 여부를 확인하고, 값에 원자 적으로 추가하고, 다중 스레드 응용 프로그램에서 추가 된 값 등을 반환하는 것입니다.

참조 URL : https://stackoverflow.com/questions/8819095/concurrency-atomic-and-volatile-in-c11-memory-model

반응형