C / C ++ 기본 유형은 원자 적입니까?
C / C ++ 기본 유형이있는 것처럼 int
, double
등, 원자, 예를 들어 스레드?
데이터 경쟁에서 자유롭습니까? 즉, 한 스레드가 그러한 유형의 객체에 쓰는 동안 다른 스레드가 객체를 읽는다면 동작이 잘 정의되어 있습니까?
그렇지 않다면 컴파일러 또는 다른 것에 의존합니까?
아니요, 기본 데이터 유형 (예 : int
, double
)은 원 자성이 아닙니다 std::atomic
.을 참조하십시오 .
대신 std::atomic<int>
또는 을 사용할 수 있습니다 std::atomic<double>
.
참고 : std::atomic
C ++ 11에서 소개되었으며 C ++ 11 이전에는 C ++ 표준이 멀티 스레딩의 존재를 전혀 인식하지 못했다는 것을 이해했습니다.
@Josh가 지적했듯이 std::atomic_flag
원자 부울 유형입니다. 전문화 와 달리 잠금 이 없음을 보장합니다std::atomic
.
인용 된 문서는 http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf 입니다. 표준이 무료가 아니므로 최종 / 공식 버전이 아니라고 확신합니다.
1.10 다중 스레드 실행 및 데이터 경쟁
- 두 표현식 평가 중 하나가 메모리 위치 (1.7)를 수정하고 다른 하나가 동일한 메모리 위치를 읽거나 수정하면 충돌합니다.
- 라이브러리는 동기화 작업으로 특별히 식별되는 여러 원자 적 작업 (29 절) 및 뮤텍스에 대한 작업 (30 절)을 정의합니다. 이러한 작업은 한 스레드의 할당을 다른 스레드에 표시하는 데 특별한 역할을합니다. 하나 이상의 메모리 위치에 대한 동기화 작업은 소비 작업, 획득 작업, 해제 작업 또는 획득 및 해제 작업 모두입니다. 연관된 메모리 위치가없는 동기화 작업은 펜스이며, 펜스 획득, 펜스 해제 또는 펜스 획득 및 해제 모두가 될 수 있습니다. 또한 동기화 작업이 아닌 완화 된 원자 작업과 특수한 특성을 가진 원자 읽기-수정-쓰기 작업이 있습니다.
(23.1)-서로 다른 스레드에 의해 수행되거나
(23.2)-순서가 지정되지 않고 적어도 하나가 신호 처리기에 의해 수행되는 경우 두 작업은 잠재적으로 동시 입니다.
프로그램의 실행은 두 개의 잠재적으로 동시에 충돌하는 작업을 포함하는 경우 데이터 경합을 포함합니다.이 중 적어도 하나는 원 자성이 아니며 아래에 설명 된 신호 처리기의 특수한 경우를 제외하고 다른 작업보다 먼저 발생하지 않습니다. 이러한 데이터 경쟁은 정의되지 않은 동작을 초래합니다.
29.5 원자 유형
- 적분 유형``숯불에 대한 원자 템플릿의 명시 적 특수화가 있어야한다
signed char
,unsigned char
,short
,unsigned short
,int
,unsigned int
,long
,unsigned long
,long long
,unsigned long long
,char16_
t는,char32_t
,wchar_t
, 및 기타 유형의 헤더의 형식 정의가 필요<cstdint>
. 각 정수 유형 적분에 대해 전문화atomic<integral>
는 적분 유형에 적합한 추가 원자 연산을 제공합니다.atomic<bool>
29.6.1에 명시된 일반 원자 연산을 제공 하는 전문화가 있어야합니다 .
- 원자 클래스 템플릿의 포인터 부분 전문화가 있어야합니다. 이러한 전문화에는 표준 레이아웃, 사소한 기본 생성자 및 사소한 소멸자가 있어야합니다. 이들은 각각 집계 초기화 구문을 지원해야합니다.
29.7 플래그 유형 및 작업
- atomic_flag 유형의 객체에 대한 작업은 잠금이 없어야합니다. [참고 : 따라서 작업에는 주소가 없어야합니다. 다른 유형에는 잠금없는 작업이 필요하지 않으므로 atomic_flag 유형은이 국제 표준을 준수하는 데 필요한 최소 하드웨어 구현 유형입니다. 나머지 유형은 이상적인 속성이 아니지만 atomic_flag로 에뮬레이션 할 수 있습니다. — 끝 참고]
C는 태그에 포함되지 않았음에도 불구하고 질문에서 (현재) 언급되기 때문에 C 표준 은 다음과 같이 명시합니다.
5.1.2.3 프로그램 실행
...
추상 기계의 처리가 신호 수신에 의해 중단 될 때,
volatile sig_atomic_t
부동 소수점 환경의 상태와 마찬가지로 잠금이없는 원자 객체도 아니고 유형도 아닌 객체의 값 은 지정되지 않습니다 . 잠금이없는 원자 객체도 아니고 유형도 아닌 핸들러에 의해 수정 된 객체의 값은volatile sig_atomic_t
핸들러에 의해 수정되고 복원되지 않은 부동 소수점 환경의 상태와 마찬가지로 핸들러가 종료 될 때 결정되지 않습니다. 원래 상태.
과
5.1.2.4 다중 스레드 실행 및 데이터 경쟁
...
두 표현식 평가 중 하나가 메모리 위치를 수정하고 다른 하나가 동일한 메모리 위치를 읽거나 수정하는 경우 두 표현식 평가가 충돌 합니다.
[여러 표준 페이지-원자 유형을 명시 적으로 다루는 일부 단락]
프로그램 실행은 서로 다른 스레드에서 두 개의 충돌하는 동작을 포함하는 경우 데이터 경합 을 포함합니다.이 중 적어도 하나는 원자 적이 지 않고 다른 작업보다 먼저 발생하지 않습니다. 이러한 데이터 경쟁은 정의되지 않은 동작을 초래합니다.
신호가 처리를 중단하는 경우 값은 "미확정"이며 명시 적으로 원 자성이 아닌 유형에 대한 동시 액세스는 정의되지 않은 동작입니다.
원자 란 무엇입니까?
원자 (Atomic)는 원자의 속성으로 무언가를 설명합니다. 원 자라는 단어는 "분할되지 않은"을 의미하는 라틴 원자에서 유래 합니다.
일반적으로 두 가지 특성을 갖는 원자 적 연산 (언어에 관계없이)을 생각합니다.
원자 적 연산은 항상 분할되지 않습니다.
I.e. it is performed in an indivisible way, I believe this is what OP refers to as "threadsafe". In a sense the operation happens instantaneously when viewed by another thread.
For example the following operation is likely divided (compiler/hardware dependent):
i += 1;
because it can be observed by another thread (on hypothetical hardware and compiler) as:
load r1, i;
addi r1, #1;
store i, r1;
Two threads doing the above operation i += 1
without appropriate synchronization may produce the wrong result. Say i=0
initially, thread T1
loads T1.r1 = 0
, and the thread T2
loads t2.r1 = 0
. Both threads increment their respective r1
s by 1 and then store the result to i
. Although two increments have been performed, the value of i
is still only 1 because the increment operation was divisible. Note that had there been synchronization before and after i+=1
the other thread would have waited until the operation was complete and thus would have observed an undivided operation.
Note that even a simple write may or may not be undivided:
i = 3;
store i, #3;
depending on the compiler and hardware. For example if the address of i
is not aligned suitably, then an unaligned load/store has to be used which is executed by the CPU as several smaller loads/stores.
An atomic operation has guaranteed memory ordering semantics.
Non atomic operations may be re-ordered and may not necessarily occur in the order written in the program source code.
For example, under the "as-if" rule the compiler is allowed to re-order stores and loads as it sees fit as long as all access to volatile memory occurs in the order specified by the program "as if" the program was evaluated according to the wording in the standard. Thus non-atomic operations may be re-arranged breaking any assumptions about execution order in a multi-threaded program. This is why a seemingly innocent use of a raw int
as a signaling variable in multi-threaded programming is broken, even if writes and reads may be indivisible, the ordering may break the program depending on the compiler. An atomic operation enforces ordering of the operations around it depending on what memory semantics are specified. See std::memory_order
.
The CPU may also re-order your memory accesses under the memory ordering constraints of that CPU. You can find the memory ordering constraints for the x86 architecture in the Intel 64 and IA32 Architectures Software Developer Manual section 8.2 starting at page 2212.
Primitive types (int
, char
etc) are not Atomic
Because even if they under certain conditions may have indivisible store and load instructions or possibly even some arithmetic instructions, they do not guarantee the ordering of stores and loads. As such they are unsafe to use in multi-threaded contexts without proper synchronization to guarantee that the memory state observed by other threads is what you think it is at that point in time.
I hope this explains why primitive types are not atomic.
An additional info I haven't seen mentioned in the other answers so far:
If you use std::atomic<bool>
, for example, and bool
is actually atomic on the target architecture, then the compiler will not generate any redundant fences or locks. The same code would be generated as for a plain bool
.
In other words, using std::atomic
only makes the code less efficient if it is actually required to for correctness on the platform. So there is no reason to avoid it.
참고URL : https://stackoverflow.com/questions/35226128/are-c-c-fundamental-types-atomic
'developer tip' 카테고리의 다른 글
github 페이지 블로그 (마크 다운)에서 disqus 댓글을 어떻게 사용하나요? (0) | 2020.12.12 |
---|---|
ReactDOM은 어디에서 가져와야합니까? (0) | 2020.12.12 |
이니셜 라이저 목록의 요소 수가 모호한 호출 오류를 일으키는 이유는 무엇입니까? (0) | 2020.12.12 |
asp.net 코어 미들웨어 대 필터 (0) | 2020.12.12 |
iPhone의 Application Delegate의 적절한 사용 (0) | 2020.12.12 |