๐ 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