std :: atomic은 정확히 무엇입니까?
나는 그것이 std::atomic<>
원자 적 객체 라는 것을 이해 합니다. 그러나 어느 정도까지 원자? 내 이해에 따르면 작업은 원자가 될 수 있습니다. 물체를 원자로 만드는 것은 정확히 무엇을 의미합니까? 예를 들어 다음 코드를 동시에 실행하는 두 개의 스레드가있는 경우 :
a = a + 12;
그렇다면 전체 작업이 add_twelve_to(int)
원 자성입니까? 아니면 변수 원자가 변경 operator=()
되었습니까?
std :: atomic <> 의 각 인스턴스화 및 전체 특수화는 정의되지 않은 동작을 발생시키지 않고 다른 스레드가 동시에 작동 할 수있는 유형을 나타냅니다.
원자 유형의 객체는 데이터 경쟁이없는 유일한 C ++ 객체입니다. 즉, 한 스레드가 원자 개체에 쓰는 동안 다른 스레드가이를 읽는 경우 동작이 잘 정의되어 있습니다.
또한 원자 객체에 대한 액세스는 스레드 간 동기화를 설정하고로 지정된 비 원자 메모리 액세스를 주문할 수
std::memory_order
있습니다.
std::atomic<>
C ++ 이전 11 번 랩핑 작업 은 GCC의 경우 MSVC 또는 원자 적 부틴 과 (예를 들어) 연동 기능 을 사용하여 수행해야했습니다 .
또한 동기화 및 순서 제약 조건을 지정 std::atomic<>
하는 다양한 메모리 순서 를 허용하여 더 많은 제어 기능을 제공합니다 . C ++ 11 원자 및 메모리 모델에 대한 자세한 내용을 보려면 다음 링크가 유용 할 수 있습니다.
- C ++ 원자 및 메모리 순서
- 비교 : C ++ 11에서 원자를 사용한 잠금없는 프로그래밍 대 뮤텍스 및 RW 잠금
- C ++ 11은 표준화 된 메모리 모델을 도입했습니다. 무슨 뜻인가요? 그리고 C ++ 프로그래밍에 어떤 영향을 미치나요?
- C ++ 11의 동시성
일반적인 사용 사례의 경우 오버로드 된 산술 연산자 또는 다른 세트를 사용합니다 .
std::atomic<long> value(0);
value++; //This is an atomic op
value += 5; //And so is this
연산자 구문을 사용하면 메모리 순서를 지정할 수 없으므로 std::memory_order_seq_cst
C ++ 11의 모든 원자 연산에 대한 기본 순서이므로이 연산은로 수행됩니다. 모든 원자 연산 간의 순차 일관성 (전체 전역 순서)을 보장합니다.
그러나 경우에 따라이 작업이 필요하지 않을 수도 있고 무료로 제공되지 않을 수도 있으므로보다 명시적인 형식을 사용하는 것이 좋습니다.
std::atomic<long> value {0};
value.fetch_add(1, std::memory_order_relaxed); // Atomic, but there are no synchronization or ordering constraints
value.fetch_add(5, std::memory_order_release); // Atomic, performs 'release' operation
자, 당신의 예 :
a = a + 12;
단일 원자 연산으로 계산하지 않을 것이다 : 그것은 초래한다 a.load()
(자체 원자이다)이 값 사이, 덧셈 12
및 a.store()
최종 결과 (도 원 참조). 앞서 언급했듯이 std::memory_order_seq_cst
여기에서 사용됩니다.
그러나을 쓰면 a += 12
원자 연산 (앞서 언급했듯이)과 거의 같습니다 a.fetch_add(12, std::memory_order_seq_cst)
.
귀하의 의견에 관해서는 :
레귤러
int
에는 원자로드 및 저장이 있습니다. 그것을 감싸는 요점은atomic<>
무엇입니까?
귀하의 진술은 상점 및 / 또는로드에 원 자성을 보장하는 아키텍처에만 적용됩니다. 이를 수행하지 않는 아키텍처가 있습니다. 또한 일반적으로 워드 / 워드 정렬 주소에서 작업을 수행해야하므로 원 자성 std::atomic<>
이 추가 요구 사항없이 모든 플랫폼에서 원 자성으로 보장됩니다 . 또한 다음과 같은 코드를 작성할 수 있습니다.
void* sharedData = nullptr;
std::atomic<int> ready_flag = 0;
// Thread 1
void produce()
{
sharedData = generateData();
ready_flag.store(1, std::memory_order_release);
}
// Thread 2
void consume()
{
while (ready_flag.load(std::memory_order_acquire) == 0)
{
std::this_thread::yield();
}
assert(sharedData != nullptr); // will never trigger
processData(sharedData);
}
Note that assertion condition will always be true (and thus, will never trigger), so you can always be sure that data is ready after while
loop exits. That is because:
store()
to the flag is performed aftersharedData
is set (we assume thatgenerateData()
always returns something useful, in particular, never returnsNULL
) and usesstd::memory_order_release
order:
memory_order_release
A store operation with this memory order performs the release operation: no reads or writes in the current thread can be reordered after this store. All writes in the current thread are visible in other threads that acquire the same atomic variable
sharedData
is used afterwhile
loop exits, and thus afterload()
from flag will return a non-zero value.load()
usesstd::memory_order_acquire
order:
std::memory_order_acquire
A load operation with this memory order performs the acquire operation on the affected memory location: no reads or writes in the current thread can be reordered before this load. All writes in other threads that release the same atomic variable are visible in the current thread.
This gives you precise control over the synchronization and allows you to explicitly specify how your code may/may not/will/will not behave. This would not be possible if only guarantee was the atomicity itself. Especially when it comes to very interesting sync models like the release-consume ordering.
I understand that
std::atomic<>
makes an object atomic.
That's a matter of perspective... you can't apply it to arbitrary objects and have their operations become atomic, but the provided specialisations for (most) integral types and pointers can be used.
a = a + 12;
std::atomic<>
does not (use template expressions to) simplify this to a single atomic operation, instead the operator T() const volatile noexcept
member does an atomic load()
of a
, then twelve is added, and operator=(T t) noexcept
does a store(t)
.
참고URL : https://stackoverflow.com/questions/31978324/what-exactly-is-stdatomic
'Programming' 카테고리의 다른 글
Java EE 컨테이너의 스폰 스레드가 권장되지 않는 이유는 무엇입니까? (0) | 2020.07.17 |
---|---|
여러 파일에서 Javascript의 전역 변수 (0) | 2020.07.17 |
파이썬에는 정렬 된 목록이 있습니까? (0) | 2020.07.17 |
RabbitMQ / AMQP : 단일 대기열, 동일한 메시지에 대한 여러 소비자? (0) | 2020.07.17 |
ImportError : 'django.core.urlresolvers'라는 모듈이 없습니다. (0) | 2020.07.16 |