프로그래밍

👨‍💻 [C++] C++ 에서 std::atomic_ 로 시작하는 데이터 타입들은 무엇이며, 어떻게 활용할 수 있나요?

밝음의기둥 2025. 1. 23. 09:28
반응형

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."


👨‍💻 안녕하세요, 윌리 프로젝트의 윌리 입니다.

C++ 에서 std::atomic_ 로 시작하는 데이터 타입들은 무엇이며, 어떻게 활용할 수 있는지 간략히 알아보았습니다.


질문 >

C++ 에서 std::atomic_ 로 시작하는 데이터 타입들은 무엇이며, 어떻게 활용할 수 있나요?

답변 >

C++의 std::atomic_* 데이터 타입들

C++ 표준 라이브러리의 <atomic> 헤더에 정의된 std::atomic_* 데이터 타입들은 멀티스레드 환경에서 원자적(atomic)으로 동작하는 변수 및 연산을 제공합니다. 원자성은 데이터가 중간 상태 없이 일관되게 읽히거나 쓰이는 것을 보장하며, 동기화 문제와 경쟁 조건(race condition)을 방지할 수 있습니다.


std::atomic_* 데이터 타입의 종류

  1. 기본 데이터 타입:
    • std::atomic<bool>: 원자적인 부울 변수.
    • std::atomic<int>: 원자적인 정수 변수.
    • std::atomic<float>: 원자적인 부동 소수점 변수.
    • std::atomic<char>: 원자적인 문자 변수.
    • std::atomic<void*>: 원자적인 포인터.
  2. 특수 데이터 타입:
    • std::atomic_flag: 가장 단순한 원자적 플래그로, 잠금 구현에 사용.
    • std::atomic<T>: 임의의 데이터 타입 T에 대해 원자적 연산을 제공.

std::atomic_*의 주요 특징

  1. 스레드 안전성:
    • std::atomic으로 선언된 변수는 동시 접근에 안전하며, 별도의 std::mutex 없이도 사용할 수 있습니다.
  2. 잠금 없이 동작:
    • 내부적으로 잠금(lock)을 사용하지 않고 하드웨어 수준의 원자적 명령어를 활용하여 동작합니다.
  3. 메모리 순서 제어:
    • C++의 원자적 연산은 메모리 순서(memory ordering)를 제어할 수 있습니다.
    • 기본적으로 순차 일관성(sequential consistency)을 보장합니다.

주요 함수와 멤버

  • 기본 연산:
    • load(): 현재 값을 읽음.
    • store(): 새로운 값 저장.
    • exchange(): 현재 값을 새로운 값으로 변경하고 이전 값을 반환.
  • 조건부 연산:
    • compare_exchange_weak(expected, desired): 조건부 교환(약한 보장).
    • compare_exchange_strong(expected, desired): 조건부 교환(강한 보장).
  • 증감 연산:
    • fetch_add(value): 값을 더하고 이전 값을 반환.
    • fetch_sub(value): 값을 빼고 이전 값을 반환.
    • ++ 또는 -: 전위/후위 증감 연산자.

사용 예제

1. 기본 사용법

#include <iostream>
#include <atomic>
#include <thread>

std::atomic<int> counter(0); // 원자적 정수

void increment() {
    for (int i = 0; i < 1000; ++i) {
        counter.fetch_add(1); // 원자적 증가
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Final counter value: " << counter << std::endl;

    return 0;
}

출력 예시:

Final counter value: 2000


2. std::atomic_flag 사용

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>

std::atomic_flag lock_flag = ATOMIC_FLAG_INIT; // 초기화된 플래그

void critical_section(int id) {
    while (lock_flag.test_and_set(std::memory_order_acquire)) {
        // 다른 스레드가 사용 중이면 기다림
    }
    // 임계 구역
    std::cout << "Thread " << id << " in critical section." << std::endl;
    lock_flag.clear(std::memory_order_release); // 플래그 해제
}

int main() {
    std::vector<std::thread> threads;

    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(critical_section, i);
    }

    for (auto& t : threads) {
        t.join();
    }

    return 0;
}

출력 예시:

Thread 0 in critical section.
Thread 1 in critical section.
Thread 2 in critical section.
Thread 3 in critical section.
Thread 4 in critical section.


3. compare_exchange 활용

#include <iostream>
#include <atomic>
#include <thread>

std::atomic<int> shared_value(10);

void worker() {
    int expected = 10;
    if (shared_value.compare_exchange_strong(expected, 20)) {
        std::cout << "Thread updated shared_value to 20." << std::endl;
    } else {
        std::cout << "Thread failed to update. Current value: " << shared_value << std::endl;
    }
}

int main() {
    std::thread t1(worker);
    std::thread t2(worker);

    t1.join();
    t2.join();

    return 0;
}

출력 예시:

Thread updated shared_value to 20.
Thread failed to update. Current value: 20


메모리 순서 제어

메모리 순서 옵션

  • std::memory_order_relaxed:
    • 순서 없이 작동. 성능은 좋지만 순차 일관성을 보장하지 않음.
  • std::memory_order_acquire:
    • 이후의 연산이 이전 연산을 기다림.
  • std::memory_order_release:
    • 이전의 모든 연산이 완료된 후 실행.
  • std::memory_order_acq_rel:
    • acquire와 release를 모두 포함.
  • std::memory_order_seq_cst (기본값):
    • 순차 일관성을 보장.

장점과 단점

장점

  1. 성능 최적화:
    • 잠금 없이 동작하여 성능과 확장성이 뛰어남.
  2. 데이터 일관성 보장:
    • 원자적 연산으로 경쟁 조건 방지.

단점

  1. 복잡한 메모리 모델:
    • 메모리 순서를 정확히 이해해야 함.
  2. 복잡성 증가:
    • 복잡한 동작 구현 시 코드 가독성이 떨어질 수 있음.

결론

C++의 std::atomic_*은 멀티스레드 프로그래밍에서 데이터 경쟁을 방지하고 효율적으로 동작하도록 도와줍니다.

  • 주요 활용:
    • 간단한 스레드 간 데이터 공유.
    • 동기화가 필요한 계산 작업.
    • 커스텀 잠금 또는 플래그 구현.

이 도구를 적절히 사용하면 안전하고 효율적인 멀티스레드 코드를 작성할 수 있습니다.



🎬 유튜브 채널 🎬

 

위로그@WiLog

📢 안녕하세요, 위로그@WiLog 시청자 여러분, 저는 윌리(Willee) 입니다. 📢 위로그@WiLog 는 자기계발을 목적으로 하는 채널 입니다. 📢 오늘도 즐겁게~ 자신을 위한 계발을 함께 해보아요~ d^_^b 📌

www.youtube.com

🎬 치지직 채널 🎬

 

위로그 채널 - CHZZK

지금, 스트리밍이 시작됩니다. 치지직-

chzzk.naver.com


반응형