동기화 기법: Atomic 연산

김 무무 ㅣ 2024. 10. 5. 00:50

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