문제

읽기, 쓰기 및 읽기 보류, 쓰기 보류 카운터를 유지하는 다중 R/W 잠금 클래스가 있습니다.뮤텍스는 여러 스레드로부터 이를 보호합니다.

내 질문은 컴파일러가 최적화를 수행하는 동안 카운터를 망치지 않도록 카운터를 휘발성으로 선언해야 하는가입니다.

아니면 컴파일러는 카운터가 뮤텍스에 의해 보호된다는 점을 고려합니까?

나는 뮤텍스가 동기화를 위한 런타임 메커니즘이고 "휘발성" 키워드가 최적화를 수행하는 동안 올바른 작업을 수행하도록 컴파일러에 대한 컴파일 시간 표시라는 것을 이해합니다.

안부, -jay.

도움이 되었습니까?

해결책

여기에는 기본적으로 관련이없는 2 개의 항목이 있으며 항상 혼란스러워합니다.

  • 휘발성 물질
  • 스레드, 잠금, 메모리 장벽 등

휘발성은 컴파일러 레지스터가 아닌 메모리에서 변수를 읽는 코드를 생성합니다. 그리고 코드를 재정렬하지 않기 위해. 일반적으로 '바로 가기'를 최적화하거나 복용하지 마십시오.

메모리 장벽 (뮤 테스, 자물쇠 등으로 제공됨) CPU 컴파일러가 어떻게 말했는지에 관계없이 읽기/쓰기 메모리 요청을 재정렬하는 것입니다. 즉, 최적화하지 않고 CPU 수준에서 바로가 컷을 사용하지 마십시오.

비슷하지만 실제로는 매우 다른 것들.

귀하의 경우, 그리고 대부분의 잠금의 경우 휘발성이 필요하지 않은 이유는 기능 호출 잠금을 위해 만들어졌습니다. 즉:

최적화에 영향을 미치는 일반 기능 호출 :

external void library_func(); // from some external library

global int x;

int f()
{
   x = 2;
   library_func();
   return x; // x is reloaded because it may have changed
}

컴파일러가 Library_Func ()를 검사하고 X를 터치하지 않는지 확인할 수 없으면 반환시 X를 다시 읽습니다. 이것은 휘발성이 없습니다.

스레딩 :

int f(SomeObject & obj)
{
   int temp1;
   int temp2;
   int temp3;

   int temp1 = obj.x;

   lock(obj.mutex); // really should use RAII
      temp2 = obj.x;
      temp3 = obj.x;
   unlock(obj.mutex);

   return temp;
}

TEMP1의 OBJ.X를 읽은 후, 컴파일러는 자물쇠의 마법 때문이 아니라 Lock () 수정 된 OBJ가 확실하지 않기 때문에 Temp2의 OBJ.X를 다시 읽게됩니다. 컴파일러 플래그를 적극적으로 최적화하도록 (알리리아 등) X를 다시 읽지 않도록 설정할 수 있지만 코드가 실패하기 시작할 수 있습니다.

Temp3의 경우 컴파일러 (희망적으로)는 OBJ.X를 다시 읽지 않습니다. 어떤 이유로 OBJ.x가 Temp2와 Temp3 사이에 변경 될 수 있다면, 휘발성을 사용하고 잠금 장치가 부러 지거나 쓸모가 없습니다).

마지막으로, lock ()/unlock () 함수가 어떻게 든 인식 된 경우, 컴파일러가 코드를 평가하고 OBJ.x가 변경되지 않는 것을 볼 수 있습니다. 그러나 여기서는 두 가지 중 하나를 보장합니다 .- 인라인 코드는 결국 일부 OS 레벨 잠금 기능 (따라서 평가 방지)을 호출하거나 컴파일러가 인식하는 __In 따라서 재정렬을 피하십시오.

편집 : PS 언급을 잊어 버렸습니다. 즉 C ++ 표준은 아직 스레드를 언급하지 않더라도 해당 컴파일러는 (적어도 최소한)입니다.

그래서 짧은 대답

휘발성이 필요하지 않습니다.

다른 팁

Herb Sutter의 기사에서 "치명적 섹션 (바람직하게는 잠금)을 사용하여 종족을 제거하십시오"(http://www.ddj.com/cpp/201804238):

따라서 재정렬 변환이 유효하려면 중요한 섹션의 주요 규칙을 준수하여 프로그램의 중요한 섹션을 존중해야합니다. 코드는 중요한 섹션에서 벗어날 수 없습니다. (코드가 들어가는 것은 항상 괜찮습니다.) 우리는 그림 1의 화살표에 의해 설명 된 임계 섹션의 시작과 끝에 대칭 일원 울타리 의미론을 요구 함으로써이 황금 규칙을 시행합니다.

  • 중요한 섹션에 들어가는 것은 획득 작전 또는 암시 적 획득 울타리입니다. 코드는 울타리가 울타리 앞에서 원래 위치에서 울타리 전에 실행하기 위해 원래 위치에서 이동할 수 없습니다. 그러나 소스 코드 순서로 울타리 앞에 나타나는 코드는 행복하게 울타리를 아래로 건너 나중에 실행할 수 있습니다.
  • 임계 섹션을 종료하는 것은 릴리스 작업 또는 암시 적 릴리스 울타리입니다. 이것은 코드가 울타리를 아래쪽으로 넘어갈 수 없으며 위쪽으로 만 막을 수 없다는 역 요건입니다. 최종 릴리스 쓰기를 보는 다른 스레드도 이전의 모든 쓰기를 볼 수 있음을 보장합니다.

따라서 컴파일러가 대상 플랫폼에 대한 올바른 코드를 생성하려면 중요한 섹션을 입력하고 종료 할 때 (그리고 중요 섹션이라는 용어는 일반적인 의미로 사용됩니다. CRITICAL_SECTION 구조 - 중요한 섹션은 다른 동기화 객체에 의해 보호 될 수 있습니다) 올바른 획득 및 릴리스 의미를 따라야합니다. 따라서 공유 변수를 보호 된 임계 섹션 내에서만 액세스하는 한 공유 변수를 휘발성으로 표시 할 필요는 없습니다.

휘발성은 위치의 현재 값을 레지스터에 로드하고 변경되지 않을 것이라고 가정하는 대신 항상 위치의 현재 값을 로드하도록 최적화 프로그램에 알리는 데 사용됩니다.이는 이중 포트 메모리 위치 또는 스레드 외부 소스에서 실시간으로 업데이트할 수 있는 위치로 작업할 때 가장 유용합니다.

뮤텍스는 컴파일러가 실제로 알지 못하는 런타임 OS 메커니즘이므로 최적화 프로그램은 이를 고려하지 않습니다.동시에 두 개 이상의 스레드가 카운터에 액세스하는 것을 방지하지만 해당 카운터의 값은 뮤텍스가 적용되는 동안에도 여전히 변경될 수 있습니다.

따라서 변수가 뮤텍스 가드 내부에 있기 때문이 아니라 외부에서 수정될 수 있기 때문에 변수를 휘발성으로 표시하는 것입니다.

휘발성을 유지하십시오.

이것은 당신이 사용하는 스레딩 라이브러리에 달려있을 수 있지만, 내 이해는 괜찮은 도서관이 ~ 아니다 사용이 필요합니다 volatile.

pthreads에서 예를 들어, 뮤텍스를 사용하면 데이터가 메모리에 올바르게 커밋되도록합니다.

편집하다: 나는 이렇게지지한다 토니의 대답 내 자신보다 낫다.

여전히 "휘발성"키워드가 필요합니다.

뮤 테스는 카운터가 동시에 접근하는 것을 방지합니다.

"Volatile"은 컴파일러가 CPU 레지스터로 캐싱하는 대신 카운터를 실제로 사용하도록 지시합니다 (동시 스레드로 업데이트되지 않음).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top