반응형
std::atomic 개념과 사용법 (C++17 기준)
1. std::atomic이란?
- 동기화된 접근을 보장하는 데이터 타입 템플릿입니다.
- 멀티스레드에서 **뮤텍스 없이도 변수 접근의 원자성(atomicity)**을 보장합니다.
- std::atomic<int>, std::atomic<bool>, std::atomic<T> 등 다양한 타입으로 사용 가능.
2. 기본 사용법
#include <atomic>
#include <iostream>
#include <thread>
std::atomic<int> counter{0};
void increment() {
for (int i = 0; i < 10000; ++i) {
counter++;
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final counter: " << counter << std::endl;
}
- counter++는 원자적으로 수행됨 → 경쟁 조건(race condition) 없이 동작
3. 주요 연산
- store(val): 값 설정
- load(): 현재 값 반환
- exchange(val): 기존 값을 새로운 값으로 바꾸고, 이전 값을 반환
- compare_exchange_strong(expected, desired)
- compare_exchange_weak(expected, desired): CAS(compare-and-swap) 연산
std::atomic<int> value{5};
int expected = 5;
bool success = value.compare_exchange_strong(expected, 10);
// 성공 시 value는 10이 되고 true 반환
4. 메모리 순서 (memory ordering)
- 기본은 memory_order_seq_cst (가장 강력하고 안전한 순서)
- 필요에 따라 memory_order_relaxed, acquire, release 등 지정 가능
counter.fetch_add(1, std::memory_order_relaxed); // 약한 메모리 순서
하지만 대부분의 경우 기본 순서를 사용하는 것으로 충분하며, 성능 최적화가 필요한 경우만 조절합니다.
5. std::atomic_flag (가장 가벼운 락)
- 간단한 락 구현이나 spin lock 등에 사용
- 초기화는 반드시 ATOMIC_FLAG_INIT로
std::atomic_flag flag = ATOMIC_FLAG_INIT;
void work() {
while (flag.test_and_set(std::memory_order_acquire)) {
// busy-wait
}
// critical section
flag.clear(std::memory_order_release);
}
6. 뮤텍스 vs atomic
항목 | mutex | atomic |
동기화 범위 | 임의의 코드 블록 | 변수 단위 |
오버헤드 | 상대적으로 큼 | 작음 |
복잡한 동작 | 가능 | 제한적 |
예외 안전성 | RAII로 안전 | 직접 관리 필요 |
7. C++17에서의 특징
- 템플릿 deduction을 사용할 수 없지만, std::atomic<T>의 인터페이스는 보다 풍부해졌습니다.
- std::atomic<std::shared_ptr<T>>도 제공되어, 참조 카운팅이 원자적으로 가능해졌습니다.
'Develop > C&CPP' 카테고리의 다른 글
[cpp] optional (0) | 2025.04.25 |
---|---|
[cpp] thread (0) | 2025.04.20 |
[cpp] mutex, lock (0) | 2025.04.20 |
[cpp] cpp17에서 달라진 점 (0) | 2025.04.20 |
[cpp] cpp14에서 추가된 것 (0) | 2025.04.20 |