반응형

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

+ Recent posts