C11 _Atomic vs Objective-C @property(atomic) — 같은 이름, 다른 세계

🔒 C11 _Atomic vs Objective-C @property(atomic)

— 같은 “atomic”이지만 전혀 다른 세계

멀티스레드 환경에서 “데이터 경쟁(race condition)”을 막기 위해 우리는 종종 atomic이라는 단어를 접합니다.

하지만 C11의 _Atomic 과 Objective-C의 @property(atomic) 은 이름만 같을 뿐, 실제 동작 원리와 목적은 완전히 다릅니다.

이 글에서는 두 개념의 기술적 차이를 Swift 개발자의 관점에서 명확히 정리해봅니다.


1️⃣ _Atomic — C11의 하드웨어 수준 원자 연산

C11 표준에서 _Atomic은 변수의 읽기/쓰기 연산을 원자적으로 보장하는 타입 수식자입니다.

즉, 해당 변수에 대한 접근이 CPU 명령어 단위로 쪼개지지 않도록 보장합니다.

#include <stdatomic.h>

_Atomic int counter = 0;

void increment() {
    atomic_fetch_add(&counter, 1);
}

여러 스레드가 동시에 increment()를 호출해도 안전합니다. atomic_fetch_add는 CPU의 lock prefix를 사용해 “한 번에 읽고, 더하고, 저장”을 처리하기 때문이죠.

  • 락(mutex) 없이도 스레드 안전
  • 매우 낮은 오버헤드
  • compare_exchange를 통해 상태 전이(CAS) 구현 가능
int expected = 0;
atomic_compare_exchange_strong(&state, &expected, 1);

“현재 상태가 0이면 1로 바꾸고, 아니면 그대로 둔다.”

이 한 줄이 완전한 스레드 안전 전이를 수행합니다.

2️⃣ @property(atomic) — Objective-C의 getter/setter 잠금

Objective-C의 @property(atomic)은 완전히 다른 개념입니다.

이는 getter/setter 호출을 잠깐 잠그는 수준의 thread-safety를 제공합니다.

@property (atomic, assign) NSInteger value;

컴파일러는 내부적으로 아래와 유사한 코드를 생성합니다.

- (NSInteger)value {
    @synchronized(self) {
        return _value;
    }
}

즉, 접근자 단위로 @synchronized 락을 걸어주는 것입니다.

따라서 읽기/쓰기 각각은 안전하지만, 읽고 수정하는 복합 연산에서는 여전히 경쟁 상태가 발생합니다.

if (obj.value == 1) {   // Thread A가 읽는 중
    obj.value = 2;      // Thread B가 동시에 수정할 수 있음 ❌
}

@property(atomic)은 “보이기만 atomic한 것”이지, 진정한 원자성(atomicity)을 제공하지 않습니다.

3️⃣ 기술적 차이 요약

구분_Atomic (C11)@property(atomic) (Objective-C)
소속C 언어 표준Objective-C 런타임
동작 수준하드웨어(CPU) 명령어 단위런타임 수준의 잠금
구현 방식lock cmpxchg 등 CPU 원자 명령내부 @synchronized 블록
성능매우 빠름 (락 없음)느림 (락 오버헤드)
보호 범위개별 연산 단위getter/setter 단위
복합 연산 안전성✅ 완전 보장 (CAS)❌ 불가능
Swift 사용C 브리징 필요Objective-C 전용

4️⃣ Swift에서 _Atomic을 활용하는 방법

Swift는 _Atomic을 직접 지원하지 않기 때문에, C11 코드를 래핑해서 사용하는 방식이 일반적입니다.

// AtomicState.h
typedef struct {
    _Atomic int v;
} atomic_state_t;

bool atomic_state_try_transition(atomic_state_t *s, int expected, int desired);
// AtomicState.swift
final class AtomicState {
    private var raw = atomic_state_t()
    func tryTransition(from: Int, to: Int) -> Bool {
        atomic_state_try_transition(&raw, from, to)
    }
}

또는 Swift 패키지 swift-atomics를 사용하면

C 코드 없이 Swift-native 방식으로 구현할 수도 있습니다.

import Atomics

let counter = ManagedAtomic(0)
counter.wrappingIncrement(ordering: .relaxed)

5️⃣ 결론: “진짜 atomic”과 “겉보기 atomic”의 구분

항목_Atomic@property(atomic)
실질적 원자성✅ 있음❌ 없음
락 사용 여부❌ 없음✅ 있음
스레드 안전성하드웨어 보장getter/setter 한정
복합 연산CAS로 안전하게 처리Race 가능성 존재

정리하자면:

  • _Atomic은 CPU가 직접 보장하는 진짜 원자성이고,
  • @property(atomic)은 getter/setter만 잠그는 겉보기 원자성입니다.

💬 마무리

C11의 _Atomic은 Lock-Free 병렬 프로그래밍의 핵심 도구입니다.

Swift 개발자라도 상태 전이, CAS 기반 동기화, 락 없는 상태 머신을 구현할 때 반드시 이해해야 할 개념입니다.

Objective-C의 @property(atomic)은 단순 접근 보호 수준에 불과하므로,

진정한 병렬 안정성을 원한다면 _Atomic 또는 swift-atomics를 사용하는 것이 바람직합니다.

Thread Safety는 복잡한 기술이 아니라, 올바른 도구 선택의 문제다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다