ยท

C11 _Atomic vs Objective-C @property(atomic) โ€” ๊ฐ™์€ ์ด๋ฆ„, ๋‹ค๋ฅธ ์„ธ๊ณ„

5 min read

๐ŸŒณ Evergreen


๐Ÿ”’ 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๋Š” ๋ณต์žกํ•œ ๊ธฐ์ˆ ์ด ์•„๋‹ˆ๋ผ, ์˜ฌ๋ฐ”๋ฅธ ๋„๊ตฌ ์„ ํƒ์˜ ๋ฌธ์ œ๋‹ค.

Leave a comment

์ด๋ฉ”์ผ ์ฃผ์†Œ๋Š” ๊ณต๊ฐœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•„์ˆ˜ ํ•„๋“œ๋Š” *๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค

Quick links

โ†‘โ†“ Navigateโ†ต OpenโŒ˜K Toggle