์ด์ œ ์šฐ๋ฆฌ๋Š” ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค! ํ•„์š”ํ•œ ๋‚ด์šฉ๋“ค์„ ์–ด๋Š์ •๋„ ๋ฐฐ์› ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ์–ด๋ ค์šด ์ด์œ ๋Š” ๋ณ„๋„์˜ ์ž‘์—…ํ๋ฆ„์œผ๋กœ ๊ณต์œ  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋Ÿฌํ•œ ๋ถ€๋ถ„์—์„œ ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ๋งˆ์ฃผํ•  ์ˆ˜ ์žˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ๋ณด์ž.

ํ•ด๋‹น ๊ธ€์€ ์‚ฌ๋‚ด ๋ฐœํ‘œ์™€ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Race Condition

๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๊ณ , ๋™์‹œ์— ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฐœ์ƒ

์ƒํ˜ธ ๋ฐฐ์ œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

NSLock

์ „์—ญ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ค‘์žฌํ•˜๊ฑฐ๋‚˜, ์ž„๊ณ„์˜์—ญ์„ ๋ณดํ˜ธํ•˜์—ฌ โ€œ์›์ž์ โ€์œผ๋กœ ์‹คํ–‰๋˜๋„๋ก ํ•  ๋•Œ ์‚ฌ์šฉ

์ž„๊ณ„ ์˜์—ญ์— ์ ‘๊ทผํ–ˆ์„ ๋•Œ, ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ. ๋ฝ์„ ํ•ด์ œํ•œ ๋’ค์— ๊ณต์œ  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

class MyObject {
    private var value = 0
    private var lock = NSLock()
 
    func increase() {
        lock.lock()
        value = value + 1
        lock.unlock()
    }
 
    func decrease() {
        lock.lock()
        value = value - 1
        lock.unlock()
    }
}

Dispatch Semaphore

๋ฆฌ์†Œ์Šค์— ๋™์‹œ์— ์ ‘๊ทผํ•˜๋Š” ์ž‘์—…์˜ ์ˆ˜๋ฅผ ์กฐ์ ˆ

signal ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด count๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ณ , wait ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถฃํ•˜๋ฉด ๊ฐ์†Œํ•œ๋‹ค.

let queue = DispatchQueue(label: "semaphore", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 3)
 
for index in 0..<100 {
    queue.async {
        semaphore.wait()
        print("task \(index)")
        sleep(1)
        semaphore.signal()
    }
}

Concurrent Queue์— ์ž‘์—…์„ 100๊ฐœ ๋„ฃ์—ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ, ์‹œ์Šคํ…œ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋ฐฐ์ •๋œ ๋ฌผ๋ฆฌ์  ์Šค๋ ˆ๋“œ์˜ ๊ฐœ์ˆ˜๋งŒํผ ์ตœ๋Œ€ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ž‘์—…์ด ์‹คํ–‰๋˜๊ณ  ๋งˆ์น˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์— semaphore๋ฅผ 3์œผ๋กœ ์žก๊ณ , wait, signal๋กœ ์ž„๊ณ„ ์˜์—ญ์— ๋“ค์–ด๊ฐˆ ๋•Œ๋งˆ๋‹ค count๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ๋‹ค. ๋˜‘๊ฐ™์ด ๋ฌผ๋ฆฌ์  ์Šค๋ ˆ๋“œ์— ์ž‘์—…์ด ๋ณ‘๋ ฌ์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์€ ๋งž์œผ๋‚˜, ํ•ด๋‹น ์ž‘์—…์ด ๋๋‚˜์ž๋งˆ์ž ์‹คํ–‰์ด ๋ ์ˆ˜๋Š” ์—†๋‹ค. semaphore๋กœ index์— ์ ‘๊ทผํ›„ ์ถœ๋ ฅํ•˜๋Š” ์ฝ”๋“œ๋ฅผ block์‹œ์ผฐ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทธ ๊ฒฐ๊ณผ 3๊ฐœ์˜ ์ž‘์—…์ด ๊ฐ ๋ฌผ๋ฆฌ ์Šค๋ ˆ๋“œ๋กœ ๋ณ‘๋ ฌ์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๊ณ , ๋ชจ๋‘ ์‹คํ–‰์ด ๋œ ๋’ค ๋‹ค์Œ ์ž‘์—…์ด ๋ฌผ๋ฆฌ ์Šค๋ ˆ๋“œ์— 3๊ฐœ๊ฐ€ ๋ฐฐ์น˜๋˜๊ณ , ๋ฌผ๋ฆฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋‚จ์•„์„œ ๋‹ค์Œ ์ž‘์—…์ด ๋ฐฐ์น˜๋˜๋”๋ผ๋„ semaphore.wait์—์„œ ๋” ๋‚ฎ์ถœ count๊ฐ€ ์—†์–ด์„œ ๋Œ€๊ธฐํ•˜๊ฒŒ ๋œ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ 1, 2, 3, 4, 5, 6 ๊ณผ ๊ฐ™์ด 3๊ฐœ์˜ ๊ฐ’๋“ค์ด ํ•œ๋ฒˆ์— 1์ดˆ๋งˆ๋‹ค ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ผ ๊ฒƒ์ด๋‹ค.

Serial Dispatch Queue

ํ์— ์ž‘์—…์ด ์ถ”๊ฐ€๋œ ์ˆœ์„œ๋Œ€๋กœ ํ•œ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์‹คํ–‰

์ด์ „์— ๋ฐฐ์› ๋˜ SerialQueue๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ• ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

class MyObject {
    private var value = 0
    private let internalQueue = DispatchQueue(label: "SerialQueue")
 
    func increase() {
        internalQueue.async {
            self.value = self.value + 1
        }
    }
 
    func decrease() {
        internalQueue.async {
            self.value = self.value - 1
        }
    }
}

์ด๋ ‡๊ฒŒ ๋‚ด๋ถ€์ ์œผ๋กœ Serial Queue๋ฅผ ํ•˜๋‚˜ ๋‘๊ณ , ์—ฌ๊ธฐ์— ์ž‘์—…์„ ๋˜์ง€๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฌธ์ œ ์ƒ๊ธธ ์š”์ง€๊ฐ€ ์—†๋‹ค.

class MyObject {
    private var internalState: Int
    private let internalQueue = DispatchQueue(label: "serial")
 
    var state: Int {
        get {
            return internalQueue.sync { internalState }
        }
 
        set (newState) {
            internalQueue.sync { internalState = newState }
        }
    }
}

๋งŒ์•ฝ ์—ฌ๊ธฐ์„œ async๋กœ ์ž‘์—…์„ ๋˜์ง€๊ณ  ์ œ์–ด๊ถŒ์„ ๋ฐ›๊ธธ ์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ฆ‰ ๋™๊ธฐ์ ์œผ๋กœ ๋™์ž‘ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด .sync ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ๋งŒ์•ฝ ์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ async๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์ง€ ์•Š๋‹ค. ์•„์˜ˆ Thread safeํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งœ๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ฑฐ๋‹ค.

๋ณดํ†ต Serial Dispatch Queue๋‚˜ .sync ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

Dispatch Barrier

DispatchQueue์—์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ž‘์—… ์‹คํ–‰์„ ๋™๊ธฐํ™”

์ด์ „์— ์ œ์ถœ๋œ ๋ชจ๋“  ์ž‘์—…์ด ์‹คํ–‰์„ ๋งˆ์น  ๋•Œ๊นŒ์ง€ Barrier Block ๋‚ด๋ถ€, ๊ทธ๋ฆฌ๊ณ  ์ดํ›„์— ์ œ์ถœ๋œ ๋ชจ๋“  ์ž‘์—…์˜ ์‹คํ–‰์„ ์ง€์—ฐํ•œ๋‹ค. ์ด๊ฑด ๋ง์ด ๋„ˆ๋ฌด ์–ด๋ ต๊ณ 

class MyObject {
    private var internalState: Int
    private let internalQueue = DispatchQueue(label: "barrier", attributes: .concurrent)
 
    var state: int {
        get {
            return internalQueue.sync { internalState } // concurrent queue์ด๋”๋ผ๋„, ์ฆ‰ ๋ฌผ๋ฆฌ์  ์Šค๋ ˆ๋“œ ์–ด๋Š๊ณณ์— ๋“ค์–ด๊ฐ€๋”๋ผ๋„ ํ•ด๋‹น ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์ง€ ์•Š์œผ๋ฉด ๋ผ์ธ์ด ์•ˆ๋„˜์–ด๊ฐ
        }
 
        set (newState) { // ํ•˜์ง€๋งŒ 
            internalQueue.async(flags: .barrier, execute: { [unowned self] in
                self.internalState = newState
            })
        }
    }
}

์ƒ์‚ฐ์ž ์†Œ๋น„์ž ํŒจํ„ด์— ์ ์ ˆํ•˜๋‹ค. queue๋ฅผ concurrentํ•˜๊ฒŒ ์„ธํŒ…ํ•˜๊ณ , setter์—์„œ ์ƒˆ๋กœ์šด ๊ฐ’์„ ์“ธ ๋•Œ, async๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ์•ˆ์— parameter๋กœ .barrier๋ฅผ ์ ์šฉํ•ด์ฃผ์—ˆ๋‹ค. barrier task๋กœ ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด, ์ˆœ๊ฐ„์ ์œผ๋กœ ํ•ด๋‹น ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ serial queue์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค. ์ฆ‰, concurrent๋กœ queue๊ฐ€ ์ง€์ •๋˜์–ด ์žˆ์ง€๋งŒ, setting์— ์žˆ์–ด์„œ๋Š” serial๋กœ ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฒƒ. ์ง€๊ธˆ์€ state์— set๋ฐ–์— ์—†์ง€๋งŒ ๋งŒ์•ฝ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋“ค์€ concurrentํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒฝ์šฐ, barrier๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ setter์˜ ๋™์ž‘์„ thread safeํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. custom queue์—์„œ๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.

์ข€ ๋” ์ง๊ด€์ ์ธ ์ดํ•ด๋ฅผ ๋•๋Š” ์ฝ”๋“œ๋ฅผ ํ•˜๋‚˜ ๊ฐ€์ ธ์™”๋‹ค.

let myQueue = DispatchQueue(label: "myQueue", attributes: .concurrent)
 
for i in 1...5 {
  myQueue.async {
      print("\(i)")
  }
}
 
myQueue.async(flags: .barrier) {
  print("barrier!!")
  sleep(5)
}
 
for i in 6...10 {
  myQueue.async {
      print("\(i)")
  }
}
 
/*
2
1
4
5
3
barrier!!
10
7
8
9
6
*/

๊ฐ‘์ž๊ธฐ barrier ๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ, ๋™์ž‘์„ ๋ฉˆ์ถ”๊ณ  ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ ๋’ค ๋‹ค์‹œ ์›์ƒ๋ณต๊ท€๋”์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

Deadlock

๋‘ ๊ฐœ ์ด์ƒ์˜ ์ž‘์—…์ด ์„œ๋กœ ์ƒ๋Œ€๋ฐฉ์˜ ์ž‘์—…์ด ๋๋‚˜๊ธฐ ๋งŒ์„ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ๋Š” ์ƒํƒœ

์ƒํ˜ธ ๋ฐฐ์ œ๋ฅผ ํ†ตํ•ด race condition์€ ๋ง‰์„ ์ˆ˜ ์žˆ์„์ง€ ๋ชจ๋ฅด์ง€๋งŒ ๋ถ€์ž‘์šฉ์œผ๋กœ ๊ต์ฐฉ ์ƒํƒœ์— ๋น ์งˆ ์ˆ˜ ์žˆ๋‹ค. ํŠน์ • ์ž‘์—…์„ ํ•˜๊ธฐ ์œ„ํ•ด x, y ์ ‘๊ทผํ•ด์•ผ ํ•˜๋Š”๋ฐ, x, y๋ฅผ ์„œ๋กœ๋‹ค๋ฅธ ๋‘๊ฐœ์˜ ์Šค๋ ˆ๋“œ์—์„œ ๊ฐ๊ฐ lock์„ ๊ฑธ์–ด๋‘”๋‹ค๋ฉด, ๋‘ ์Šค๋ ˆ๋“œ ๋ชจ๋‘ ์ž‘์—…์ด ๋ถˆ๊ฐ€ํ•œ ์ƒํƒœ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

Serial Dispatch Queue + Sync

let queue = DispatchQueue(lable: "deadlock")
 
queue.sync {
    queue.sync {
        // Deadlock
    }
}

๋‚ด๋ถ€ ์ž‘์—…์ด ๋๋‚˜์•ผ ๋ฐ”๊นฅ ์ž‘์—…์„ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ณ , ์ฝ”๋“œ๊ฐ€ ์ง„ํ–‰๋œ๋‹ค. ํ•˜์ง€๋งŒ ๋‚ด๋ถ€์— sync ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์–ด์„œ ์‹คํ–‰ํ•˜๋ ค๊ณ  ๋ณด๋‹ˆ, ๋ฐ”๊นฅ block์ด ์ง„ํ–‰๋˜์ง€ ์•Š์•„ ๋‚ด๋ถ€ ์ž‘์—…์„ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์—†๋‹ค. ๊ต์ฐฉ์ƒํƒœ์— ๋น ์ง„๋‹ค. serial queue๋ฅผ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ ์ž‘์—…์”ฉ๋งŒ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

let queue = DispatchQueue(lable: "deadlock")
 
queue.async {
    queue.sync {
        // Deadlock
    }
}

์ผ๋‹จ ์ œ์–ด๊ถŒ์€ ๋ฐ›์•„์„œ ์ฝ”๋“œ๊ฐ€ ๋‚ด๋ ค๊ฐ€๋ ค ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ์ด์ œ ์‹ค์ œ๋กœ ์ž‘์—…์ด ๋“ค์–ด๊ฐ„ Queue์—์„œ๋Š” ์ƒํ™ฉ์ด ๋‹ค๋ฅด๋‹ค. ๋‚ด๋ถ€ task๋ฅผ ์ง„ํ–‰ํ•ด์•ผ ๋ฐ”๊นฅ task๊ฐ€ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๊ณ , ๋ฐ˜๋Œ€๋กœ ๋‚ด๋ถ€ task๊ฐ€ ์ˆ˜ํ–‰๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ”๊นฅ task๊ฐ€ ์ข…๋ฃŒ๋˜์–ด์•ผ ํ•œ๋‹ค.

DispatchQueue.main.sync {
    // Deadlock
}

์ฝ”๋“œ ๋ผ์ธ์€ main thread์—์„œ ์ฝ๋Š”๋ฐ, ์•ˆ์ชฝ์ด ์‹คํ–‰๋˜์–ด์•ผ ๋ฐ”๊นฅ์ชฝ์ด ์‹คํ–‰๋˜๊ณ , ๋ฐ”๊นฅ์ชฝ์€ ์•ˆ์ชฝ์ด ๋‹ค ์‹คํ–‰๋˜์•ผ ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.

Operation Queue + Dependencies

let operationA = BlockOperation {
    print("operationA")
}
 
let operationB = BlockOperation { 
    print("operationB")
}
 
let queue = OperationQueue()
operationA.addDependency(operationB)
operationB.addDependency(operationA)
queue.addOperations([operationA, operationB], waitUntilFinished: true)

Operation์„ ์„œ๋กœ ์ข…์†์œผ๋กœ ๋งŒ๋“ค์–ด ๋‘์–ด, ์„œ๋กœ ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.

Dispatch Precondition

์‹คํ–‰์— ํ•„์š”ํ•œ Dispatch ์กฐ๊ฑด์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฐ ์ƒํ™ฉ ์ž์ฒด๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜์ด๋‹ค.

dispatchPrecondition(condition: .onQueue(DispatchQueue))
dispatchPrecondition(condition: .notOnQueue(DispatchQueue))
dispatchPrecondition(condition: .onQueueAsBarrier(DispatchQueue))

๋’ค์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์กฐ๊ฑด์— ๋งž์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰์„ ์ค‘์ง€ํ•ด๋ฒ„๋ฆฐ๋‹ค.

class DispatchPreconditionExample {
    func executeTaskOnMainQueue() {
        dispatchPrecondition(condition: .onQueue(.main))
 
        print("๋ฉ”์ธ ํ์—์„œ ์‹คํ–‰ ์•ˆ๋˜๊ณ  ์žˆ๋Š”๋ฐ์š”?")
    }
}
 
let example = DispatchPreconditionExample()
let queue = DispatchQueue.global()
 
queue.async {
    example.executeTaskOnMainQueue() // Error
}

์ •๋ฆฌ

  • Race Condition ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ณดํ†ต Serial ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • Deadlock ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์ƒํ™ฉ์„ ์•Œ์•„๋‘์–ด์•ผ ํ•œ๋‹ค.
  • DispatchPrecondition์œผ๋กœ ํ•ด๋‹น ์ž‘์—…์ด ์–ด๋Š queue์—์„œ ์ผ์–ด๋‚˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

Reference