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. 1๏ธโƒฃ์˜ Task๊ฐ€ ๋จผ์ € actor์— ์ ‘๊ทผํ•˜์—ฌ image ํ˜ธ์ถœ
  2. await ํ‚ค์›Œ๋“œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋™์ž‘์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด suspend๋จ
  3. cache์— ์•„์ง ์—†๊ธฐ ๋•Œ๋ฌธ์— downloadImage ํ˜ธ์ถœ
  4. 2๏ธโƒฃ์˜ Task๊ฐ€ ์‹คํ–‰๋˜๊ณ , actor์— ์ ‘๊ทผํ•˜์—ฌ image ํ˜ธ์ถœ
  5. await ํ‚ค์›Œ๋“œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋™์ž‘์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด suspend๋จ
  6. 1๏ธโƒฃ์˜ ๊ฒฐ๊ณผ๊ฐ€ ์•„์ง caching๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— downloadImage ํ˜ธ์ถœ
  7. 1๏ธโƒฃ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™€์„œ image ๋ณ€์ˆ˜์— ํ• ๋‹น๋จ
  8. 2๏ธโƒฃ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™€์„œ image ๋ณ€์ˆ˜์— ํ• ๋‹น๋จ
  9. 1๏ธโƒฃ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ UI update๋ฅผ ์‹œ๋„ํ•จ
  10. ๊ทธ๋Ÿฐ๋ฐ 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 ๋ฌธ์ œ๋Š” ๊ฐœ๋ฐœ์ž์˜ ์ฑ…์ž„์ด๋‹ค.

Reference