C ++ 읽기 및 쓰기는 int 원자입니까?


81

나는 두 개의 스레드, 하나는 int를 업데이트하고 다른 하나는 그것을 읽는다. 읽기 및 쓰기 순서가 관련이없는 통계 값입니다.

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

예를 들어 0x00010000의 증가 된 값을 가져 오는 값 = 0x0000FFFF를 생각해보십시오.

걱정해야 할 값이 0x0001FFFF처럼 보이는 시간이 있습니까? 확실히 유형이 클수록 이와 같은 일이 더 많이 발생할 수 있습니다.

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


5
정말? 커뮤니티가 어떻게 생각하든 상관하지 않습니다. 나는 :) 사실이 무엇인지 관심을 것입니다
sehe

1
주제에 대한 흥미로운 읽기 : channel9.msdn.com/Shows/Going+Deep/…
ereOn

답변:


47

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


ACE_Atomic_Op 문서가 이동되었습니다. 이제 dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ace/Atomic_Op.inl
Byron

63

소년, 무슨 질문입니다. 이에 대한 대답은 다음과 같습니다.

예, 아니요, 음, 상황에 따라 다릅니다.

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

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


23
휘발성 키워드는 멀티 스레드 프로그램에서 유용하지 않습니다 stackoverflow.com/questions/2484980/...

5
@IngeHenriksen : 그 링크에 확신이 없습니다.
Skizz 2012-07-05

불행히도 아주 오래된 다른 소스,하지만 (이것은 표준 : 원자 선행) : web.archive.org/web/20190219170904/https://software.intel.com/...
최대 Barraclough


9

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

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


3

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

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

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



2

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


0

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


0

나는 많은 사람들, 특히 Jason에 동의합니다 . Windows에서는 InterlockedAdd와 그 친구들을 사용합니다.


0

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

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

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



0

이 예를 들어 보겠습니다.

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

첫 번째 명령문은 단일 CPU주기를 사용하는 단일 INC 어셈블리 지시문으로 변환되기 때문에 원자 적이라고 가정합니다. 그러나 두 번째 할당에는 여러 작업이 필요하므로 분명히 원자 적 작업이 아닙니다.

또 다른 예,

x=5;

다시 말하지만, 여기서 정확히 어떤 일이 발생하는지 확인하려면 코드를 분해해야합니다.


2
그러나 컴파일러는이를 x+=6.
tc.

0

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


0

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

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

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


1
찢어짐이 없기 때문에 하나의 스레드 만 변수에 쓰는 경우에는 문제가되지 않습니다.
Ben Voigt

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.