1. Atomic 연산
중간 상태 없이 한 번에 완전히 수행되는 연산
다른 스레드가 연산 중간에 끼어들 수 없음을 보장한다.
특징:
- 불가분성 (Indivisibility): 연산이 완전히 수행되거나 전혀 수행되지 않음
- 일관성 (Consistency): 다른 스레드에서 중간 상태를 관찰할 수 없음
- 순서성 (Ordering): 메모리 순서 (Memory Ordering) 보장
2. Atomic 변수 사용법
C++11부터 <atomic> 헤더를 통해 atomic 타입을 제공한다.
ex)
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> num(0);
void Increment() {
for (int i = 0; i < 1000000; i++) {
num++; // atomic 증가 연산
}
}
int main() {
std::thread t1(Increment);
std::thread t2(Increment);
t1.join();
t2.join();
std::cout << "값: " << num << std::endl;
}
주요 atomic 연산:
store() | 값 저장 |
load() | 값 로드 |
exchange() | 값 교환 |
compare_exchange_weak() compare_exchange_strong() |
비교 후 교환 |
ex)
std::atomic<int> num(0);
// 저장
num.store(10);
// 로드
int current = num.load();
// 교환
int old_value = num.exchange(20);
// 비교 후 교환
int expected = 20;
bool exchanged = num.compare_exchange_strong(expected, 30);
3. Atomic 연산의 한계
1) 제한된 데이터 타입:
- 기본적으로 정수형, bool형, 포인터 타입에 대해서만 완전한 atomic 연산을 지원
- 복잡한 객체의 경우 std::atomic<T>를 사용할 수는 있지만, lock-free를 보장하지 않는다.
2) 성능 오버헤드:
- Atomic 연산은 일반 연산보다 느릴 수 있다.
- 특히 compare_exchange 연산은 상당한 오버헤드가 있을 수 있다.
3) 메모리 순서 (Memory Ordering) 고려:
- 기본적으로 `memory_order_seq_cst`(가장 엄격한 순서)를 사용하지만, 성능을 위해 다른 메모리 순서를 선택할 수 있다.
- 하지만 순서를 바꾸면 복잡성을 증가시키고 버그 발생 가능성을 높인다.
4) 복합 연산의 한계:
- 여러 atomic 변수를 동시에 조작해야 하는 경우, atomic 연산만으로는 충분하지 않을 수 있다.
- 이런 경우 mutex 등 다른 동기화 메커니즘과 함께 사용해야 한다.
Atomic 연산은 단순한 공유 데이터에 대한 동기화에 매우 효과적이다.
하지만 복잡한 동기화의 경우 다른 방법과 함께 사용해야 한다.
'멀티스레딩' 카테고리의 다른 글
동기화 기법: Mutex (0) | 2024.10.05 |
---|---|
동기화 기법: Lock (0) | 2024.10.05 |
메모리 구조와 멀티스레딩 (0) | 2024.10.03 |
데이터 동기화 기초 (0) | 2024.10.01 |
스레드 생성과 관리 (0) | 2024.09.28 |