문제

두 개의 스레드가 있는데 하나는 int를 업데이트하고 다른 하나는 읽는 스레드입니다.읽기, 쓰기 순서가 무관한 통계값입니다.

제 질문은 어쨌든 이 멀티바이트 값에 대한 액세스를 동기화해야 합니까?입니다.또는 달리 말하면 쓰기의 일부가 완료되어 중단된 다음 읽기가 발생할 수 있습니다.

예를 들어 값이 0x00010000으로 증가하는 값 = 0x0000FFFF를 생각해 보세요.

값이 0x0001FFFF처럼 보이는 경우가 있는데 걱정해야 합니까?확실히 유형이 클수록 이와 같은 일이 발생할 가능성이 더 높습니다.

저는 항상 이러한 유형의 액세스를 동기화해 왔지만 커뮤니티에서는 어떻게 생각하는지 궁금했습니다.

도움이 되었습니까?

해결책

처음에는 기본 머신 크기의 읽기 및 쓰기가 원자적이라고 생각할 수 있지만 프로세서/코어 간의 캐시 일관성을 포함하여 처리해야 할 많은 문제가 있습니다.Windows에서는 Interlocked*와 같은 원자적 작업을 사용하고 Linux에서는 이에 상응하는 작업을 사용합니다.C++0x는 이를 멋진 크로스 플랫폼 인터페이스로 래핑하기 위한 "원자적" 템플릿을 갖습니다.현재로서는 플랫폼 추상화 계층을 사용하는 경우 이러한 기능을 제공할 수 있습니다. 에이스 그렇습니다. 클래스 템플릿을 참조하세요. ACE_Atomic_Op.

다른 팁

정말 질문이군요.이에 대한 대답은 다음과 같습니다.

예, 아니요, 흠, 글쎄요, 상황에 따라 다릅니다

그것은 모두 시스템 아키텍처에 달려 있습니다.IA32에서는 올바르게 정렬된 주소가 원자성 연산이 됩니다.정렬되지 않은 쓰기는 원자성일 수 있으며 사용 중인 캐싱 시스템에 따라 다릅니다.메모리가 단일 L1 캐시 라인 내에 있으면 원자성이며, 그렇지 않으면 원자성이 아닙니다.CPU와 RAM 사이의 버스 너비는 원자적 특성에 영향을 미칠 수 있습니다.8086에서 올바르게 정렬된 16비트 쓰기는 원자성인 반면, 8088에서는 동일한 쓰기가 원자성이 아니었습니다. 8088에는 8비트 버스만 있는 반면 8086에는 16비트 버스가 있었기 때문입니다.

또한 C/C++를 사용하는 경우 공유 값을 휘발성으로 표시하는 것을 잊지 마십시오. 그렇지 않으면 최적화 프로그램은 변수가 스레드 중 하나에서 업데이트되지 않는다고 생각할 것입니다.

4바이트 값을 읽고 쓰고 있고 메모리에서 DWORD로 정렬되어 있으며 I32 아키텍처에서 실행 중인 경우 읽기 및 쓰기는 원자성입니다.

예, 액세스를 동기화해야 합니다.C++0x에서는 데이터 경쟁이 발생하고 정의되지 않은 동작이 발생합니다.POSIX 스레드를 사용하면 이미 정의되지 않은 동작입니다.

실제로 데이터 유형이 기본 단어 크기보다 크면 잘못된 값을 얻을 수 있습니다.또한 읽기 및/또는 쓰기 이동 최적화로 인해 다른 스레드에서는 작성된 값을 볼 수 없습니다.

동기화해야 하지만 특정 아키텍처에서는 이를 수행하는 효율적인 방법이 있습니다.

가장 좋은 방법은 조건에 따라 구현을 플랫폼별 구현으로 대체할 수 있도록 서브루틴(아마도 매크로 뒤에 가려져 있음)을 사용하는 것입니다.

Linux 커널에는 이미 이 코드 중 일부가 있습니다.

Windows에서 Interlocked***Exchange***Add는 원자성이 보장됩니다.

위층에서 모두가 말한 내용을 반영하기 위해 C++0x 이전 언어는 여러 스레드의 공유 메모리 액세스에 대해 어떤 것도 보장할 수 없습니다.모든 보장은 컴파일러에 달려 있습니다.

확실히 NO!그 대답은 우리의 최고 C++ 권위자인 M.후원:
"일반" 변수에 대한 연산은 원자성이 보장되지 않습니다.

아니요, 그렇지 않습니다(또는 적어도 그렇다고 가정할 수는 없습니다).그렇긴 하지만, 이를 원자적으로 수행하는 몇 가지 트릭이 있지만 일반적으로 이식성이 없습니다(참조 비교 및 교환).

나는 많은 사람들의 의견에 동의하며 특히 제이슨.Windows에서는 InterlockedAdd와 그 친구들을 사용할 가능성이 높습니다.

위에서 언급한 캐시 문제 외에도...

레지스터 크기가 더 작은 프로세서로 코드를 이식하면 더 이상 원자성이 없습니다.

IMO, 스레딩 문제는 위험을 감수하기에는 너무 까다롭습니다.

이 예를 들어보자

int x;
x++;
x=x+5;

첫 번째 문은 단일 CPU 주기를 사용하는 단일 INC 어셈블리 지시문으로 변환되므로 원자성인 것으로 간주됩니다.그러나 두 번째 할당에는 여러 작업이 필요하므로 원자적 작업이 아닙니다.

또 다른 예를 들면,

x=5;

여기서도 정확히 무슨 일이 일어나는지 보려면 코드를 분해해야 합니다.

TC, 나는 당신이 상수 (6과 같은)를 사용하는 순간, 한 기계주기에서 명령어가 완료되지 않을 것이라고 생각합니다.x++와 비교하여 x+=6 명령 세트를 확인해보세요.

어떤 사람들은 ++c가 원자적이라고 생각하지만 생성된 어셈블리를 주시합니다.예를 들어 'gcc -S'를 사용하면 다음과 같습니다.

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

int를 증가시키기 위해 컴파일러는 먼저 이를 레지스터에 로드하고 메모리에 다시 저장합니다.이것은 원자적이지 않습니다.

유일한 이식 가능한 방법은 컴파일러의 signal.h 헤더에 정의된 sig_atomic_t 유형을 사용하는 것입니다.대부분의 C 및 C++ 구현에서 이는 int입니다.그런 다음 변수를 "휘발성 sig_atomic_t"로 선언합니다.

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