문제

2개의 스레드가 실행되는 응용 프로그램이 있습니다.한 스레드에서 전역 변수를 변경하면 다른 스레드에서도 이 변경 사항을 알 수 있다는 보장이 있습니까?동기화나 상호 배제 시스템이 없어요...하지만 이 코드가 항상 작동해야 할까요? 부울 명명 된 데이터업데이트됨):

스레드 1:

while(1) {
    if (dataUpdated)
        updateScreen();
    doSomethingElse();
}

스레드 2:

while(1) {
    if (doSomething())
        dataUpdated = TRUE;
}

gcc와 같은 컴파일러는 전역 값을 확인하지 않고 컴파일 타임의 값만 고려하는 방식으로 이 코드를 최적화합니까(동일한 스레드에서 변경되지 않기 때문입니다)?

추신:게임과 같은 애플리케이션의 경우 값이 기록되는 동안 읽기가 있는지 여부는 실제로 중요하지 않습니다.중요한 것은 다른 스레드가 변경 사항을 알아차린다는 것입니다.

도움이 되었습니까?

해결책

예.아니요.아마도.

첫째, 다른 사람들이 언급했듯이 dataUpdated를 휘발성으로 만들어야 합니다.그렇지 않으면 컴파일러는 루프에서 자유롭게 읽을 수 있습니다(doSomethingElse가 이를 건드리지 않는다는 것을 볼 수 있는지 여부에 따라 다름).

둘째, 프로세서 및 주문 요구 사항에 따라 메모리 배리어가 필요할 수 있습니다.휘발성은 다른 프로세서가 결국 변경 사항을 볼 것이라고 보장하기에 충분하지만 변경 사항이 수행된 순서대로 표시된다는 것을 보장하기에는 충분하지 않습니다.귀하의 예에는 플래그가 하나만 있으므로 이 현상이 실제로 표시되지 않습니다.메모리 배리어가 필요하고 사용하는 경우 더 이상 휘발성이 필요하지 않습니다.

유해한 것으로 간주되는 휘발성 그리고 Linux 커널 메모리 장벽 근본적인 문제에 대한 좋은 배경이 있습니다.나는 스레딩을 위해 특별히 작성된 비슷한 내용을 실제로 모릅니다.고맙게도 스레드는 하드웨어 주변 장치만큼 자주 이러한 문제를 제기하지 않지만 설명하는 종류의 경우(플래그가 설정된 경우 유효한 것으로 추정되는 다른 데이터와 함께 완료를 나타내는 플래그)는 정확히 주문하는 종류의 것입니다. 중요하다...

다른 팁

다음은 부스트 ​​조건 변수를 사용하는 예입니다.

bool _updated=false;
boost::mutex _access;
boost::condition _condition;

bool updated()
{
  return _updated;
}

void thread1()
{
  boost::mutex::scoped_lock lock(_access);
  while (true)
  {
    boost::xtime xt;
    boost::xtime_get(&xt, boost::TIME_UTC);
    // note that the second parameter to timed_wait is a predicate function that is called - not the address of a variable to check
    if (_condition.timed_wait(lock, &updated, xt))
      updateScreen();
    doSomethingElse();
  }
}

void thread2()
{
  while(true)
  {
    if (doSomething())
      _updated=true;
  }
}

자물쇠를 사용하세요.공유 데이터에 접근하려면 항상 잠금 장치를 사용하세요.변수를 휘발성으로 표시하면 컴파일러가 메모리 읽기를 최적화하는 것을 방지할 수 있지만 다음과 같은 다른 문제를 예방할 수는 없습니다. 메모리 재정렬.잠금이 없으면 doSomething()에 작성된 메모리가 updateScreen() 함수에 표시된다는 보장이 없습니다.

유일한 안전한 방법은 다음을 사용하는 것입니다. 기억의 울타리, 예를 들어 명시적으로 또는 암시적으로 Interlocked* 기능을 사용합니다.

사용 휘발성 물질 값이 언제든지 변경될 수 있음을 컴파일러에 암시하는 키워드입니다.

volatile int myInteger;

위의 내용은 변수에 대한 모든 액세스가 특정 최적화 없이 메모리를 통해 이루어지도록 보장하며 결과적으로 동일한 프로세서에서 실행되는 모든 스레드는 코드가 읽는 것과 동일한 의미로 변수에 대한 변경 사항을 "확인"합니다.

Chris Jester-Young은 다중 프로세서 시스템에서 이러한 가변 값 변경에 대한 일관성 문제가 발생할 수 있다고 지적했습니다.이는 고려 사항이며 플랫폼에 따라 다릅니다.

실제로 플랫폼과 관련하여 고려해야 할 두 가지 고려 사항이 있습니다.그것은 메모리 트랜잭션의 일관성과 원자성입니다.

원자성은 실제로 단일 프로세서 플랫폼과 다중 프로세서 플랫폼 모두에서 고려 사항입니다.이 문제는 변수가 본질적으로 멀티바이트일 가능성이 높으며 하나의 스레드가 값에 대한 부분 업데이트를 볼 수 있는지 여부가 문제이기 때문에 발생합니다.즉:일부 바이트 변경, 컨텍스트 전환, 스레드 중단으로 인해 잘못된 값 읽기.자연어 크기 이하이고 자연스럽게 정렬되는 단일 변수의 경우에는 문제가 되지 않습니다.구체적으로, 정수 유형은 정렬되어 있는 한 항상 OK여야 합니다. 이는 컴파일러의 기본 사례여야 합니다.

일관성과 관련하여 이는 다중 프로세서 시스템에서 잠재적인 문제입니다.문제는 시스템이 프로세서 간에 완전한 캐시 일관성을 구현하는지 여부입니다.구현된 경우 이는 일반적으로 하드웨어의 MESI 프로토콜을 사용하여 수행됩니다.질문에는 플랫폼이 명시되지 않았지만 Intel x86 플랫폼과 PowerPC 플랫폼은 모두 일반적으로 매핑된 프로그램 데이터 영역에 대해 프로세서 전체에서 캐시 일관성을 유지합니다.따라서 이러한 유형의 문제는 프로세서가 여러 개 있는 경우에도 스레드 간의 일반 데이터 메모리 액세스에 대해 문제가 되지 않습니다.

발생하는 원자성과 관련된 마지막 문제는 읽기-수정-쓰기 원자성과 관련이 있습니다.즉, 값이 업데이트되어 읽혀지고 쓰여지는 경우 이것이 원자적으로 발생한다는 것을 어떻게 보장할 수 있습니까? 프로세서가 두 개 이상인 경우에도 마찬가지입니다.따라서 특정 동기화 개체 없이 이것이 작동하려면 변수에 액세스하는 모든 잠재적 스레드가 읽기 전용이어야 하지만 한 번에 하나의 스레드만 쓰기가 될 수 있어야 합니다.그렇지 않은 경우 변수에 대한 읽기-수정-쓰기 작업에 대한 원자적 작업을 보장하려면 사용 가능한 동기화 개체가 필요합니다.

귀하의 솔루션은 무엇보다도 CPU를 100% 사용합니다.Google에서 "조건 변수"를 검색합니다.

Chris Jester-Young은 다음과 같이 지적했습니다.

이는 Java 1.5+의 메모리 모델에서만 작동합니다.C++ 표준은 스레딩을 다루지 않으며 휘발성은 프로세서 간의 메모리 일관성을 보장하지 않습니다.이를 위해서는 메모리 장벽이 필요합니다.

그렇다면 유일한 정답은 동기화 시스템을 구현하는 것 뿐이겠죠?

사용 휘발성 물질 값이 언제든지 변경될 수 있음을 컴파일러에 암시하는 키워드입니다.

volatile int myInteger;

아니요, 확실하지 않습니다.변수를 휘발성으로 선언하면 컴파일러는 읽기 시 항상 메모리에서 변수를 로드하는 코드를 생성해야 합니다.

범위가 올바른 경우("extern", global 등)) 그러면 변경 사항이 표시됩니다.문제는 언제인가?그리고 어떤 순서로?

문제는 컴파일러가 ~할 수 있다 그리고 자주 ~ 할 것이다 성능 최적화를 위해 모든 동시 파이프라인을 채우도록 논리 순서를 변경하세요.

할당에 대한 다른 지침이 없기 때문에 특정 예에서는 실제로 표시되지 않지만 bool 할당 실행 후에 선언된 함수를 상상해 보세요. ~ 전에 과제.

점검 파이프라인 위험 Wikipedia에서 "컴파일러 명령 재정렬"을 Google에서 검색하세요.

다른 사람들이 말했듯이 volatile 키워드는 당신의 친구입니다.:-)

gcc에서 모든 최적화 옵션을 비활성화했을 때 코드가 작동할 가능성이 높습니다.이 경우 (내 생각에는) 모든 것을 휘발성으로 취급하고 결과적으로 모든 작업에 대해 변수가 메모리에 액세스됩니다.

모든 종류의 최적화가 활성화되면 컴파일러는 레지스터에 보관된 로컬 복사본을 사용하려고 시도합니다.기능에 따라 이는 변수의 변경 사항이 간헐적으로만 표시되거나 최악의 경우 전혀 변경되지 않음을 의미할 수 있습니다.

키워드 사용 volatile 이 변수의 내용은 언제든지 변경될 수 있으며 변경되어야 함을 컴파일러에 나타냅니다. ~ 아니다 로컬로 캐시된 복사본을 사용하세요.

그 모든 것을 말하면 더 나은 결과를 찾을 수 있습니다. 제프) 세마포어 또는 조건 변수를 사용하여.

이것 주제에 대한 합리적인 소개입니다.

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