Task๋ ์ฝ๋ ๋ธ๋ญ์ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. ์ด๋ฌํ ์ ์ ์ฌ์ฉํ๋ฉด ๋ณ๋ ฌ์ ์ผ๋ก ํน์ ์ฝ๋๋ฅผ ์ฒ๋ฆฌํ๊ฒ ํ ์๋ ์๋ค. ๊ทธ๋ฐ๋ฐ, ๋ ๊ฐ ์ด์์ Task์์ ๊ณต์ ์์์ ์ฌ์ฉํด์ผ ํ๋ค๋ฉด ์ด๋ป๊ฒ ํ ๊น?
๊ณต์ ์์์ ์ ๊ทผํ๋ ๊ฒ์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ์ผ์ผํค๋ ํต์ฌ์ ์ธ ์ด์ ์ค ํ๋๋ค. ๊ถ๊ธํ๋ค๋ฉด 13. Concurrency์์ ๊ทธ ์ด์ ๋ค์ ์์๋ณด์. Apple์ ์ด๋ฌํ ๋ฌธ์ ์ ๋ํด Actor๋ผ๋ ํ์ ์ ๋ง๋ค์ด ๋ฌธ์ ๋ฅผ ์์ฒ๋ด์ํ๊ณ ์ ํ๋ค. ์ด๋ Actor Model์์ ๋์ค๋ ๊ฐ๋ ์ด๋ค. ํ๋ฒ ์์๋ณด์.
Data races make concurrency hard
class Counter {
var value = 0
func increment() -> Int {
value = value + 1
return value
}
}
let counter = Counter()
Task.detached { // detached๋ฅผ ์ฌ์ฉํ๋ฉด unstructured concurrency๋ก ๋์ํ๋ค. ๋ค์๊ธ์์ ์ดํด๋ณด๊ฒ ๋ค.
print(counter.increment())
}
Task.detached {
print(counter.increment())
}๊ณต์ ๋ณ์์ ๋ Task๊ฐ ์ ๊ทผํ์ฌ, ์์ ์ ๋ฐ๋ผ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค๋ ๋ฌธ์ ๊ฐ ์๋ค. ๋ Task ๋ชจ๋ 1์ ๋ฐ๊ฑฐ๋, ๋๋ค 2๋ฅผ ๋ฐ๊ฑฐ๋, 1, 2๋ฅผ ๋ฐ๊ฑฐ๋ ํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค๋ ๋ง์ด๋ค.
์ด๋ฐ ๋ฌธ์ ๋ ๋ณ๊ฒฝ๊ฐ๋ฅํ ๊ณต์ ์์์ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ์ดํฐ๊ฐ ๊ณต์ ๋์ง ์๊ฑฐ๋, ๋ค์ค ์ค๋ ๋์์ ํด๋น ๋ณ์๊ฐ ๊ณต์ ๋์ง ์๋๋ค๋ฉด data race๋ ๋ฐ์ํ์ง ์๋๋ค.
Does Value Semantic Solve Data race?
Value semantic์ ์ฌ์ฉํ์ฌ ๊ณต์ ์์ฒด๋ฅผ ํ์ง ์๋๋ก ํ๋ค.
let์ฌ์ฉ- struct ์ฌ์ฉ
var array1 = [1, 2]
var array2 = array1 // copyOnWrite๋ก ์์ง ๋ณต์ฌ ์๋จ
array1.append(3) // ์ด ์์ ์ ๋ณ๊ฒฝ๋จ
array2.append(4)
print(array1) // 1, 2, 3
print(array2) // 1, 2, 4
// ๋ณต์ฌ ์๋ฃvalue type์ ์ฌ์ฉํ๊ฒ ๋๋ฉด, ๊ฐ์ ๋ณต์ฌํด์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์์ ํ๋ค. array, dictionary ๋ฑ์ด ์๊ฐ ๋ ์ ์๊ฒ ๋ค.
struct Counter {
var value = 0
mutating func increment() -> Int {
value = value + 1
return value
}
}
let counter = Counter()
Task.detached {
print(counter.increment()) // โ Cannot use mutating member on immutable value 'counter' is let constant.
}
Task.detached {
print(counter.increment()) // โ Cannot use mutating member on immutable value 'counter' is let constant.
}ํ์ง๋ง ์ด ๋ฐฉ์์ Counter์ ๋์
ํ๋ฉด ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํ๋ค. ๋จผ์ let์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, immutable ๋ณ์์ member ๋ณ์๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค๋ ์๋ฌ๊ฐ ๋ฌ๋ค.
struct Counter {
var value = 0
mutating func increment() -> Int {
value = value + 1
return value
}
}
var counter = Counter()
Task.detached {
print(counter.increment()) // โ Mutation of captured var 'counter' in concurrently-executing code
}
Task.detached {
print(counter.increment()) // โ Mutation of captured var 'counter' in concurrently-executing code
}๊ทธ๋ผ var๋ก ๋ณ๊ฒฝํ๋ฉด ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋ ๊น? ๋น์ฐํ ์๋๋ค. ๊ฒฐ๊ตญ ๋ด๋ถ์ ์๋ value๋ผ๋ property์ ๊ฐ์ ๋ณ๊ฒฝํ๊ณ ์๋ ํ์๋ ๊ฐ๊ธฐ ๋๋ฌธ์, concurrentํ๊ฒ ์ ๊ทผํ๊ณ ์์ด ๋ฌธ์ ๊ฐ ์์ ์ ์๋ค๋ ์๋ฌ๋ฅผ ๋ฟ๋๋ค.
๊ฒฐ๊ตญ Value semantic์ผ๋ก ๊ณต์ ๋ณ์์ ์ ๊ทผํ๋ ๊ฒ์ ํด๊ฒฐํ ์ ์์๋ค. multi thread๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ด๋ฌํ ๊ณต์ ๋ณ์์ ์ ๊ทผํด์ ๋ก์ง์ ์ฒ๋ฆฌํด์ผ ํ๋ ๊ฒ์ ํ์ฐ์ ์ด๋ค.
Shared mutable state in concurrent programs
์ด๋ ๊ฒ ๋๋ ๊ณต์ ์์์ ์ ๊ทผํ๋ ๋ฐฉ์์ ๋ํด ๊ณ ์ฐฐํ ํ์๊ฐ ์๋ค.
- ๋ณ๊ฒฝ๊ฐ๋ฅํ ๊ณต์ ์ํ๋ ๋๊ธฐํ๋ฅผ ํ์๋ก ํ๋ค.
- Lock, Serial dispatch queues, Atomics์ ๊ฐ์ low level ๋๊ตฌ๋ค์ด ์๋ค.
- ๊ฐ๊ฐ์ ๊ฐ์ ์ด ์์ผ๋, ๋ชจ๋ ๊ฐ์ง๋ ์น๋ช ์ ์ธ ๋จ์ ์ด ์๋ค.
- ์ ํํ๊ณ ์ณ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์งฌ์ด ์์ด์ผ ํ๋ค.
Actors
์ด๋ฐ ์ํฉ์์ Actor๊ฐ ๋์๋ค.
- ๋ณ๊ฒฝ๊ฐ๋ฅํ ๊ณต์ ์ํ์ ๋ํด ๋๊ธฐํ๋ฅผ ์ ๊ณตํ๋ค.
- ํ๋ก๊ทธ๋จ์ผ๋ก ๋ถํฐ ๋
๋ฆฝ๋ ์ํ๋ฅผ ๊ฐ์ง๋ค.
- ๊ฐ์ ์ ๊ทผํ๊ธฐ ์ํด์๋ actor๋ฅผ ํตํด์๋ง ๊ฐ๋ฅํ๋ค.
- Actor๋ ํน์ ์ํ์ ๋ํด ์ ๊ทผํ๋๋ฐ ์์ด ์ํธ๋ฐฐ์ ๋ฅผ ๋ณด์ฅํ๋ค.
- Compiler๊ฐ ์ด๋ฌํ ์ ์์ Error๋ฅผ ๋์ ธ์ฃผ์ด, ๋ฐ์ํ ์ ์๋ data race์ ๋ํด ์๋ ค์ค๋ค. ์ด๊ฑธ ๋ฐํ์ผ๋ก actor๋ก ๋ณ๊ฒฝํ๋ฉด ๋๋ค.
actor Counter {
var value = 0
func increment() -> Int {
value = value + 1
return value
}
}
let counter = Counter()
Task.detached {
print(await counter.increment()) // 1, 2
}
Task.detached {
print(await counter.increment()) // 2, 1
}Actor๋ ๋ค์์ ํน์ง์ ๊ฐ๋๋ค.
- structs, enums, classes์ ๋น์ทํ ๊ธฐ๋ฅ์ ๊ฐ์ง๋ค.
- property, method, initailzier, subscript etc.
- protocol ์ฑํ ๊ฐ๋ฅ
- extension๋ ๊ฐ๋ฅ
- Reference type
- ๊ณต์ ๋์ด ์ฌ์ฉ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ
- ๊ฐ์ฅ ํฐ ์ฐจ๋ณ๋๋ ํน์ง์, actor์ instance data๋ฅผ ํ๋ก๊ทธ๋จ์ ๋๋จธ์ง ๋ถ๋ถ๊ณผ ๊ฒฉ๋ฆฌํ๋ค๋ ๊ฒ์ด๋ค.
- ๊ทธ๋ฆฌ๊ณ ์ด๋ก์ ํด๋น ๋ฐ์ดํฐ์ ๋๊ธฐํ๋ ์ ๊ทผ์ ๋ณด์ฅํ๋ค.
์ฆ, ์์ Counter ์ฝ๋์์ Actor๋ก ๋ณ๊ฒฝ๋ ์ดํ, increment๋ฅผ ์ด๋์์ ํธ์ถํ๋๋ผ๋, ํด๋น ๋์์ ์๋ฃ๋ ์ดํ์ ์์ฐจ์ ์ผ๋ก ๋ค์ task๋ฅผ ์ฒ๋ฆฌํ๋ค๋ ๊ฒ์ด๋ค.
Asynchronous interaction with actors
actor์ ๋น๋๊ธฐ์ ์ผ๋ก ์ํธ์์ฉํ๋ค๋ ๊ฒ์, actor์ ์ธ๋ถ์์ method๋ฅผ ์ฌ์ฉํ ๋๋ฅผ ๋งํ๋ค.
actor Counter {
var value = 0
func increment() -> Int {
value = value + 1
return value
}
}
let counter = Counter()
Task.detached {
print(await counter.increment()) // 1, 2
}
Task.detached {
print(await counter.increment()) // 2, 1
}actor์ ์ ๊ทผํ์ ๋, ์ด๋ฏธ ์ด์ ๋์์ด ์งํ์ค์ด๋ผ๋ฉด, ๋์์ ์์ฒญํ Task๋ ์ฒ๋ฆฌ๋ฅผ suspendํ๊ณ ๋ค๋ฅธ ์์
์ ์ฒ๋ฆฌํ๋ค. actor์์ ์ด์ ์์
์ด ์๋ฃ๋์๋ค๋ฉด actor์ ์์
์ฒ๋ฆฌ ์์ฒญ์ด ๋ค์ด๊ฐ๊ณ , ์๋ฃ๋๋ฉด Task๊ฐ resume๋๋ค. ์ด๋ awaitํค์๋๋ก ํ์ฌ๊ธ async ์ฒ๋ฆฌ๊ฐ actor์์ ์งํ๋๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
Synchronous interaction with actors
๊ทธ๋ผ ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋๋ค๋ ๊ฒ์ ๋ฌด์์ ๋งํ๋ ๊ฑธ๊น? actor๋ด๋ถ์์ ๊ฐ์ ๋ณ๊ฒฝ์ํค๋ ํจ์๋ฅผ ํธ์ถํ์ ๋๋ฅผ ๋งํ๋ค.
extension Counter {
func resetSlowly(to newValue: Int) {
value = 0
for _ in 0..<newValue {
increment()
}
assert(value == newValue)
}
}resetSlowly ํจ์๋ ๋ด๋ถ ๋ณ์์ธ value๋ฅผ newValue๋ก ์ด๊ธฐํํ๋ ํจ์์ด๋ค. Counter actor์์ ์ ์๋์ด ์์ผ๋ฉฐ, ์ด๋ ๊ณง ์ง์ ์ ์ผ๋ก value์ ์ ๊ทผํ ์ ์๋ค๋ ๋ง์ด๋ค. ์ด๋ฐ ๊ฒฝ์ฐ์๋ ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ await ๊ฐ ํ์์๋ค.
์ฆ, actor์์ ์๋ ํจ์๋ ๋๊ธฐ์ ์ผ๋ก ํธ์ถ๋๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ ๊ฒ ์คํ๋๋ ๋๊ธฐ ์ฝ๋๋ ๋ฐฉํด๋ฐ์ง ์๊ณ ์คํ๋๋ค.
Actor Reentrancy Problem
์์์ actor ๋ด๋ถ์์ ํธ์ถํ๋ ์ฝ๋์ ๊ฒฝ์ฐ syncํ๊ฒ ๋์ํ๋ค๊ณ ํ์ผ๋, ์ค์ ๋ก๋ ์ข ์ข async ์ฝ๋์ ์ํธ์์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
actor ImageDownloader {
private var cache: [URL: Image] = [:]
func image(from url: URL) async throws -> Image? {
if let cached = cache[url] {
return cached
}
let image = try await downloadImage(from: url)
cache[url] = image
return image
}
}์ผ๋จ ์ด ์ฝ๋์ ๊ฒฝ์ฐ์๋ actor์์์ ํธ์ถํ๊ณ ์๊ธฐ ๋๋ฌธ์, data race ์ํ๊ฐ ๋ฐ์ํ์ง ์๋๋ค. ์์์ ๋งํ๋ฏ ๋๊ธฐ์ ์ผ๋ก ์์ฐจ์ ์ด๊ฒ ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฐ๋ฐ, ์ด image(from:) ํจ์๋ฅผ ์ธ๋ถ task์์ ํธ์ถํ๋ฉด ์ด๋ค์ผ์ด ๋ฐ์ํ ๊น?
let downloader = ImageDownloader()
let url = URL("https://someUrl~")
Task.detached { // 1๏ธโฃ
let image = await downloader.image(from: url)
// UI Updates...
}
Task.detached { // 2๏ธโฃ
let image = await downloader.image(from: url)
// UI Updates...
}- 1๏ธโฃ์ Task๊ฐ ๋จผ์ actor์ ์ ๊ทผํ์ฌ
imageํธ์ถ awaitํค์๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ํด๋น ๋์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๊ธฐ ์ํด suspend๋จ- cache์ ์์ง ์๊ธฐ ๋๋ฌธ์
downloadImageํธ์ถ - 2๏ธโฃ์ Task๊ฐ ์คํ๋๊ณ , actor์ ์ ๊ทผํ์ฌ
imageํธ์ถ awaitํค์๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ํด๋น ๋์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๊ธฐ ์ํด suspend๋จ- 1๏ธโฃ์ ๊ฒฐ๊ณผ๊ฐ ์์ง caching๋์ง ์์๊ธฐ ๋๋ฌธ์
downloadImageํธ์ถ - 1๏ธโฃ์ ๊ฒฐ๊ณผ๊ฐ ๋์์
image๋ณ์์ ํ ๋น๋จ - 2๏ธโฃ์ ๊ฒฐ๊ณผ๊ฐ ๋์์
image๋ณ์์ ํ ๋น๋จ - 1๏ธโฃ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ์ผ๋ก UI update๋ฅผ ์๋ํจ
- ๊ทธ๋ฐ๋ฐ 8์์์ ๊ฒฐ๊ณผ๋ก ๋ณ๊ฒฝ๋์ด, ์๋ ์๋(7์์ ๋ฐ์ ์ด๋ฏธ์ง)์๋ ๋ค๋ฅธ ์ด๋ฏธ์ง(์ด๋ฏธ์ง์ ๋ชจ์์ ๊ฐ์ ์ ์์ง๋ง ๋ค์ด๋ก๋๋ฅผ ํ๋ฒ ๋ ์ํํ ๋ ์์)(8)๊ฐ ํ๋ฉด์ ๋์ด
์ฌ๊ธฐ์ ๋ฌธ์ ๋, actor ๋ด๋ถ์ ์ ์๋ ํจ์๋ syncํ๊ฒ ๋์ํ๋, ๊ทธ ํจ์ ๋ด๋ถ์ ๋น๋๊ธฐ ํจ์๊ฐ ํฌํจ๋ ๊ฒฝ์ฐ, syncํ๊ฒ ๋์ํ์ง ์์ ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ ๋์ถ๋ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์ฌ์ค ์์ ์์์ ๊ฐ์ cache์ ๊ฒฝ์ฐ ๊ฐ์ url์ด๋ผ๋ฉด ๋๋ฒ downloadimage ํ ํ์๊ฐ ์๋ค. ์ด๋ฌํ ์๋, ์ํ์ ์
๊ธ, ์ถ๊ธํ ๋๋ ๋ฐ์ํ ์ ์๋ค. ๋์ ์
๊ธํ๊ณ , ์ถ๊ธํ๋ ๋์์ ํ๋ ํจ์๊ฐ ์๋ค๊ณ ํ ๋, ์๋ก ๋ค๋ฅธ ์คํ ์ฃผ์ฒด์์ ํด๋น ํจ์๋ฅผ ํธ์ถํ๋ค๋ฉด ์์ image ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ์ฒ๋ผ ์
์ถ๊ธ์ ์์, ํน์ ํ์์๋ ์คํ ๋ฑ์ด ๊ฒน์ณ ์ํ๋ ๊ฒฐ๊ณผ๊ฐ ๋์ค์ง ์์ ์ ์๋ค.
์ด๋ฐ ์ํฉ์ ์ ํ์ ์ธ Reentrancy Problem์ด๋ค.
Reentrancy: ์๋ธ๋ฃจํด์ด ๋์์(๋ณ๋ ฌ) ์์ ํ๊ฒ ์คํ ๊ฐ๋ฅํ ์ํ. ์ฆ, ์ฌ์ง์ ์ด ๊ฐ๋ฅํ ๋ฃจํด์ ๋์์ ์ ๊ทผํด๋ ์ธ์ ๋ ๊ฐ์ ์คํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํ๋ค.
Example
actor BankAccount {
private var balance = 1000
func withdraw(_ amount: Int) async {
print("๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: \(amount)")
guard canWithdraw(amount) else {
print("๐ซ ์ถ๊ธ์ ์ถฉ๋ถํ ์์ก์ด ์์ต๋๋ค. ์๊ณ : \(balance)")
return
}
guard await authorizeTransaction() else { // ์๋ฒ์์ transaction ํ์ธ ์ฒ๋ฆฌํ๋๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ mocking
return
}
print("โ
๊ฑฐ๋ ์ฒ๋ฆฌ ์๋ฃ: \(amount)์์ ๋ฐ์์ฃผ์ธ์.")
balance -= amount
print("๐ฐ ๋จ์ ๊ณ์ข ์๊ณ : \(balance)์")
}
private func canWithdraw(_ amount: Int) -> Bool {
return amount <= balance
}
private func authorizeTransaction() async -> Bool {
// Wait for 1 second
try? await Task.sleep(nanoseconds: 1 * 1000000000)
return true
}
}
let account = BankAccount()
Task {
await account.withdraw(800)
}
Task {
await account.withdraw(500)
}actor๋ก ๊ณ์ข๋ฅผ ๋ง๋ค์๊ณ , ๋ด๋ถ์ ์๊ณ property๋ฅผ ํตํด ์ถ๊ธ์ ํ๋ ์๋๋ฆฌ์ค๋ฅผ ๊ตฌ์ฑํ๋ค. ๋๊ฐ์ Task์์ ๊ฐ์ง ์๊ณ ๋ณด๋ค ๋ ๋ง์ ๊ธ์ก์ ์ถ๊ธํ๋ ๊ฒ์ ์๋ํ๋ค. ์๋ฏธ์ ์ผ๋ก ์ํ๋ ๊ฒฐ๊ณผ๋, ๋ Task์ค ์ด๋ค ๊ฒ์ด ๋จผ์ ์คํ๋๋ , ๋ ์ค ํ๋๋ ์๊ธ์ด ๋ถ์กฑํด ๋์ ์ธ์ถํ ์ ์์ด์ผ ํ๋ค.
๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: 800
๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: 500
โ
๊ฑฐ๋ ์ฒ๋ฆฌ ์๋ฃ: 800์์ ๋ฐ์์ฃผ์ธ์.
๐ฐ ๋จ์ ๊ณ์ข ์๊ณ : 200์
โ
๊ฑฐ๋ ์ฒ๋ฆฌ ์๋ฃ: 500์์ ๋ฐ์์ฃผ์ธ์.
๐ฐ ๋จ์ ๊ณ์ข ์๊ณ : -300์
์คํจ๋ค. ์ํ์ ๊ณง ํ์ฐํ ์ง๋ ๋ชจ๋ฅธ๋ค.
Designing Actor for Reentrancy
Apple์ ์์์ ๋ณด๋ฉด, actor Reentrancy๋ deadlock์ ๋ฐฉ์งํ์ฌ, ์งํํ๋๋ก์ ํ ์ ์๋ค๊ณ ํ๋ค. ํ์ง๋ง await๋ก ์ ์ธ๋ ํจ์์ ๋๊ธฐ์๊ฐ์ ๊ฐ์ ํด์ผ ํ๋ค. ์ฆ, actor ๋ด๋ถ์ mutable state์ ๋ํ ๊ด๋ฆฌ๊น์ง๋ ํด์ฃผ์ง ์๋๋ค. ๊ทธ๊ฑด ๊ฐ๋ฐ์์ ์ฑ
์์ด๋ค.
ํด๊ฒฐํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
Perform State Mutation in Synchronous Code
Apple ์์ง๋์ด๋ค์ด ์ ์ํ๋ ์ฒซ๋ฒ์งธ ๋ฐฉ๋ฒ์, actor ๋ด๋ถ ์ํ ๋ณ๊ฒฝ์ sync code ์์์ ํ๋ผ๋ ๊ฒ์ด๋ค.

์ฌ๊ธฐ์ ๋ฌธ์ ๋ ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์๋ ํ๋ฆ์์ ๋จผ์ ์ถ๊ธ ๊ฐ๋ฅ์ฌ๋ถ๋ฅผ ํ๋จํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ฒ์์๋ ์ถ๊ธ ๊ฐ๋ฅ์ด๋ผ ํต๊ณผ๋ ์ดํ, ์๋ฒ์ ์์ฒญ์ ํ๋ ๋์ suspend๋๊ณ , ๊ทธ ์ฌ์ด์ ๋๋ฒ์งธ Task์ ์ถ๊ธ ๊ฐ๋ฅ ๋ก์ง์ ์ํํ๋ค. ๋น์ฐํ ์์ง ๋ฐ์์ด ์๋์์ผ๋ ํต๊ณผ๋๋ฒ๋ฆฐ๋ค. ์ด ์์ ์์ ์ฌ์ค ํต๊ณผ๋๋ฉด ์๋๋ค.
actor BankAccount {
private var balance = 1000
func withdraw(_ amount: Int) async {
guard await authorizeTransaction() else { // ์๋ฒ ์์ฒญ์ ๋จผ์ ์ํํ๋ค.
return
}
print("๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: \(amount)")
guard canWithdraw(amount) else {
print("๐ซ ์ถ๊ธ์ ์ถฉ๋ถํ ์์ก์ด ์์ต๋๋ค. ์๊ณ : \(balance)")
return
}
print("โ
๊ฑฐ๋ ์ฒ๋ฆฌ ์๋ฃ: \(amount)์์ ๋ฐ์์ฃผ์ธ์.")
balance -= amount
print("๐ฐ ๋จ์ ๊ณ์ข ์๊ณ : \(balance)์")
}
}๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: 800
โ
๊ฑฐ๋ ์ฒ๋ฆฌ ์๋ฃ: 800์์ ๋ฐ์์ฃผ์ธ์.
๐ฐ ๋จ์ ๊ณ์ข ์๊ณ : 200์
๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: 500
๐ซ ์ถ๊ธ์ ์ถฉ๋ถํ ์์ก์ด ์์ต๋๋ค. ์๊ณ : 200
์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์๋ ๋จผ์ ์๋ฒ์ ์์ฒญ์ ํ ํ(async ํจ์๋ฅผ ๋จผ์ ํธ์ถํ๊ณ ) ์ถ๊ธ ๊ฐ๋ฅ ํ์ธ ๋ก์ง๊ณผ ์ค์ ์ธ์ถ์ ๊ฐ์ ๋๊ธฐ ์ฝ๋ ์์์ ๋ฌถ์ด๋ฒ๋ฆฌ๋ฉด ๋๋ค. ๊ทธ๋ ๊ฒ ๋๋ฉด ๋๋ฒ์ Task๋ actor ์์ ์๋ ํจ์์ ์ ์๋ ๋์์ด๊ธฐ ๋๋ฌธ์ synchronousํ๊ฒ ๋์ํ๋ค. (์์ Synchronous interaction with actors์ ์์ ์ค๋ช ํจ)
Check the Actor State After a Suspension Point
๊ทผ๋ฐ ์ฌ์ค ๋ฌธ์ ๊ฐ ์๋ค. ๊ณ์ข์ ์์ก์ด ๋ถ์กฑํ๋ฉด, ์๋ฒ์ Transaction ํ์ธํ ํ์๊ฐ ์์๊น? ์์ก์ด ๋ถ์กฑํ๋ฉด early returnํ์ฌ ๋ถํ์ํ ์์ฒญ์ ํ์ง ์๋ ๊ฒ์ด ๋ ์ข์ ๊ฒ ์๋๊ฐ? (์ฌ์ง์ ์ ์ ๊ฑฐ)
์ด๋ป๊ฒ ํ๋ฉด ์ฌ๋ฌ๋ฒ์ ์ธ์ถ ์์ฒญ์ multi thread ํ๊ฒฝ์์ ๊น๋ํ๊ฒ ์ฒ๋ฆฌํ๋ฉด์ Reentrancy ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์๊น? ์ด๋ฌํ ๋ฌธ์ ์ ๋ํด Apple ์์ง๋์ด๋ค์ด ๋ด๋๋ ๋ต์, suspension point, ์ฆ, await์ ๊ด๋ จ๋ ํจ์๋ฅผ ํธ์ถํ ๋ค์, ์ํ๋ฅผ ํ์ธํ๋ผ๋ ๊ฒ์ด๋ค. ์ฝ๊ฒ ๋งํ๋ฉด transation ์์ฒญ ํ์ ๋ค์ ์ถ๊ธ ๊ฐ๋ฅ์ฌ๋ถ๋ฅผ ํ๋จํ๋ผ๋ ๊ฒ์ด๋ค.
func withdraw(_ amount: Int) async {
guard canWithdraw(amount) else { // ์ผ๋จ early return์ ์ํด ํ๋ฒ ๊ฑธ๋ฌ์ค๋ค.
print("๐ซ ์ถ๊ธ์ ์ถฉ๋ถํ ์์ก์ด ์์ต๋๋ค. ์๊ณ : \(balance)")
return
}
guard await authorizeTransaction() else { // ์๋ฒ ์์ฒญ์ ๋จผ์ ์ํํ๋ค.
return
}
print("โ
Transaction์ด ์น์ธ๋์์ต๋๋ค.")
print("๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: \(amount)")
guard canWithdraw(amount) else {
print("๐ซ ์ถ๊ธ์ ์ถฉ๋ถํ ์์ก์ด ์์ต๋๋ค. ์๊ณ : \(balance) - after authorized")
return
}
print("โ
๊ฑฐ๋ ์ฒ๋ฆฌ ์๋ฃ: \(amount)์์ ๋ฐ์์ฃผ์ธ์.")
balance -= amount
print("๐ฐ ๋จ์ ๊ณ์ข ์๊ณ : \(balance)์")
}์ด๋ ๊ฒ ํ๋ฉด ์ ์ฝ๋์ ๊ฐ์ด Task๊ฐ ๊ฑฐ์ ๋น์ทํ ์๊ฐ๋์ ๋์ํ๋ ๊ฒฝ์ฐ์๋ early stop์ด ๋จนํ์ง ์๊ฒ ์ง๋ง, ์ ์ฉํ ์๋ ์๋ค.
Thread Safety vs. Reentrancy
Actor๊ฐ Thread-safe ํ๋ค๋ ๊ฒ์, actor๊ฐ ๊ฐ์ง ๋ณํ๊ฐ๋ฅํ ์ํ์ ๋ํด ์ํธ ๋ฐฐ์ ๋ฅผ ๋ณด์ฅํ๋ค๋ ๊ฒ์ด๋ค. ์ฆ, ํน์ ์ํ์ ๋ํด ๋์์ ์ ๊ทผํ๋ ๊ฒ์ ๋ง๊ฒ ๋ค๋ ๊ฒ์ด๋ค. ์์ account ์ฝ๋๋ฅผ ๋ณด์์ ๋, ํญ์ ๐ค ์ถ๊ธํ ๊ธ์ก์
๋๋ค: 800์ด ๋จผ์ ์ถ๋ ฅ๋์๋ค. ์ฆ, ์ธ๋ถ์์ ์ฌ๋ฌ Task ๋ด๋ถ์ actor์ method๋ฅผ ํธ์ถํ๋๋ผ๋, ์ฌ๋ฌ๊ฐ ์กด์ฌํ๋ Task์์ ๋
์์ ์ธ ์คํ ํ๋ฆ์ ๊ฐ์ง๊ณ ๋ฉ์๋๋ฅผ ์คํํ๋ ๊ฒ์ด ์๋, actor๊ฐ ๊ฐ์ง๋ ์คํํ๋ฆ์ ์ข
์๋๋ค๋ ๊ฒ์ด๋ค.

Reentrancy ๋ฌธ์ ๊ฐ ๋ณดํต multi thread ์ํฉ์์ ๋ฐ์ํ์ง๋ง thread-safe์๋ ๋ค๋ฅธ ๋ฌธ์ ์ด๋ค. ์ฌ๊ธฐ์ ๊ณจ์๋, actor๋ผ๋ ํ์
์ด multi thread์์ ๋ฐ์ํ๋ ๋ค์ํ ๋ฌธ์ ๋ ์ฒ๋ฆฌํ์ง๋ง, ์ฌ์ ํ ์ฌํธ์ถํ๊ฑฐ๋, ์ฌ๋ฌ ๋ค๋ฅธ task์์ ์คํํ์ ๋ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง ์ ์๋ค๋ ์ ์ ๋งํ๊ณ ์๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ด์ ๋, suspension point(await)์์ actor์ ์ํ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์์ ๊ฒ์ด๋ผ๊ณ ๊ฐ์ ํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ค. ๋ถ๋ช
actor ๋ด๋ถ์ ์ ์๋ ํจ์๋ syncํ๊ฒ ๋์ํ์ง๋ง, ๋ด๋ถ์ ๋น๋๊ธฐ ํจ์๋ฅผ ๋ฃ๊ณ ์คํํ๊ฒ ๋๋ฉด, ๊ทธ ๋น๋๊ธฐํจ์์ ๋์์ ๋ค๋ฅธ ์คํ ํ๋ฆ์ ๊ฐ๊ณ ์๊ณ , ๊ทธ๋์ ํด๋น ๋น๋๊ธฐ ํจ์๋ฅผ ํธ์ถํ ์คํ ํ๋ฆ์ suspend๋๋ค. ๊ทธ๋ฐ๋ฐ concurrentํ๊ฒ ์๋ํ๊ธฐ ๋๋ฌธ์ suspend๋ ๋์ ๋ค๋ฅธ ์์
์ ์ํํ๊ฒ ๋๊ณ , ๊ทธ ์์
์ด ๊ฐ์ actor์ ํจ์๋ฅผ ํธ์ถํ๊ฒ ๋๋ค๋ฉด, ์ด์ Task์ ๊ฒฐ๊ณผ๊ฐ ๋์ถ๋๊ธฐ ์ ์ ํธ์ถ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์ค์ง ์์ ์ ์๋ค.
์ฆ, Thread-safe์ Reentrancy๋ ๋ค๋ฅธ ๋ฌธ์ ๋ค.
๋ง๋ฌด๋ฆฌ
Reentrancy์ ๋ํด์๋ ๋ง๋ฒ์ ์ ๋ต์ด ์๋ค. ์ํฉ๋ง๋ค ๋ค๋ฅด๊ณ ์ฒ๋ฆฌํด์ผ ํ๋ ๋ฐฉ์๋ ๋ค๋ฅด๋ค. ํ์ง๋ง actor์ ๋ํด ์์๋ณด๋ฉด์ ์๊ฐํด๋ณผ๋งํ ๋ถ๋ถ์ ๋ถ๋ช ํ ์์๋ค.
- actor ์ธ๋ถ์์ ํธ์ถํ๋ ๊ฒฝ์ฐ await ํค์๋๋ฅผ ํตํด ๊ฒฐ๊ณผ ๋ฐํ์ ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
- actor ๋ด๋ถ์ ์ ์๋ ํจ์๋ syncํ๊ฒ ๋์ํจ์ ๋ณด์ฅํ๋ค.
- ํ์ง๋ง actor ๋ด๋ถ์
await์ ๊ฐ์ suspension์ ๊ฑธ๊ณ , ์ด๋ฅผ ์ธ๋ถ์์ ํธ์ถํ๋ค๋ฉด Reentrancy ๋ฌธ์ ์ ๋น ์ง ์ ์๋ค. - Reentrancy ๋ฌธ์ ๋ ์๋ธ๋ฃจํด์ด ๋์์(๋ณ๋ ฌ) ์์ ํ๊ฒ ์คํ ๊ฐ๋ฅํ ์ํ๋ฅผ ๋งํ๋ค.
- actor๋ dead lock์ด ๋ฐ์ํ๋ ๊ฒ์ ๋ง์์ค ์ ์๋ค. ํ์ง๋ง ์์ ๊ฐ์ Reentrancy ๋ฌธ์ ๋ ๊ฐ๋ฐ์์ ์ฑ ์์ด๋ค.