Skip to content

Commit 444d535

Browse files
committed
Add Actor Thumbnail
1 parent 68b7300 commit 444d535

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

Public/thumbnail/actor.svg

Lines changed: 4 additions & 0 deletions
Loading

Sources/Website/Contents/Posts/actor.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,23 @@ description: Actor에 대해서 알아보겠습니다.
77
---
88

99
### Actor 란
10+
1011
`Actor`는 Swift Concurrency에서 Data Race를 컴파일 타임에 방지하기 위해 도입된 reference 타입입니다.
1112

1213
Actor가 나오기 전엔, class는 여러 스레드에서 동시에 접근할 수 있기 때문에, 개발자가 직접 `DispatchQueue`, `NSLock`, `Semaphore` 등을 통해서 동기화를 보장해야했지만, 그로 인해 유지보수 비용이 매우 높아졌다.
1314

1415
하지만 Actor는 이를 언어 차원에서 해결해준다.
1516

16-
1717
### Actor의 핵심 보장
18+
1819
- 한 시점에 단 하나의 Task만 actor의 mutable state에 접근 가능
1920
- 동기화 코드를 직접 작성하지 않아도 됨
2021
- 잘못된 동시 접근은 컴파일 에러로 차단
2122

2223
즉 Actor는 Thread-safe한 class를 기본값으로 제공한다고 하면 이해하기 쉽다.
2324

2425
### Actor의 기본 구조
26+
2527
```swift
2628
actor Counter {
2729
var value: Int = 0
@@ -53,9 +55,10 @@ Task {
5355

5456
`actor` 내부의 `var`은 자동으로 보호되며 외부에서 접근시 반드시 `await`가 필요하고, 내부에서는 `await` 없이 자기 자신의 state 접근 가능하다.
5557

56-
5758
### Actor Isolation
59+
5860
Actor에서는 Isolation이 존재한다.
61+
5962
- actor의 모든 mutable state는 actor의 executor에 격리되게 된다.
6063
- 외부에서는 actor 내부 상태에 직접 접근이 불가하다.
6164

@@ -85,6 +88,7 @@ actor UserStore {
8588

8689
await store.add("Jihoon")
8790
```
91+
8892
이 구조 덕분에 데이터 레이스는 구조적으로 불가능합니다.
8993

9094
### Actor vs Class + Lock
@@ -103,14 +107,16 @@ final class SafeCounter {
103107
}
104108
}
105109
```
110+
106111
문제점
112+
107113
- lock 누락 가능성
108114
- 데드락 위험
109115
- 가독성 저하
110116
- 테스트 난이도 상승
111117

112-
113118
Actor 방식을 사용하면
119+
114120
```swift
115121
actor SafeCounter {
116122
private var value = 0
@@ -144,6 +150,7 @@ actor Config {
144150
- mutable state 접근은 불가
145151

146152
### Reentrancy (재진입성)
153+
147154
Actor는 reentrant하다.
148155

149156
```swift
@@ -161,14 +168,16 @@ actor BankAccount {
161168

162169
문제는, `await` 중에 다른 task가 끼어들 수 있고, 논리적 경쟁 상태 (logical race) 발생 가능합니다.
163170
이를 해결하려면, 중요한 연산은 `await` 없이 한 번에 처리하며, State snapshot 사용하면 된다.
171+
164172
```swift
165173
func withdraw(_ amount: Int) {
166174
guard balance >= amount else { return }
167175
balance -= amount
168176
}
169177
```
170-
178+
171179
### Global Actor (MainActor)
180+
172181
```swift
173182
@MainActor
174183
class ViewModel {
@@ -179,20 +188,24 @@ class ViewModel {
179188
특정 executor(주로 Main Thread)에 격리 시킬수 있습니다.
180189

181190
### 성능
191+
182192
Actor같은 경우
193+
183194
- lock 기반 보다 약간 오버헤드가 존재
184195
- context switching 비용
185196
- executor enqueue/dequeue
186197

187198
하지만
199+
188200
- 대부분의 앱에서 체감 불가 수준
189201
- lock 사용에 대한 버그 비용보다는 오버헤드가 감수 할 정도
190202

191203
Actor를 사용하면 안되는 경우
204+
192205
- 초당 수십만번 호출 되는 hot path 경우
193206
- 매우 짧은 atomic 연산
194207

195-
대신
208+
대신
196209
`ManagedAtomic` (Swift Atomics)와 value type + task-local 로 처리 하면 됩니다.
197210

198211
### 언제 사용해야되나
@@ -202,8 +215,8 @@ Actor를 사용하면 안되는 경우
202215
- Analytics, Logging
203216
- ViewModel, Domain Service
204217

205-
206218
### 피해야되는 경우
219+
207220
- 수치 연산 중심 코드
208221
- tight looop
209222
- 단일 스레드 보장 환경

0 commit comments

Comments
 (0)