์ด์ ์ฐ๋ฆฌ๋ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ํ ์ ์๋ค! ํ์ํ ๋ด์ฉ๋ค์ ์ด๋์ ๋ ๋ฐฐ์ ๋ค๊ณ ํ ์ ์๋ค. ํ์ง๋ง, ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ด ์ด๋ ค์ด ์ด์ ๋ ๋ณ๋์ ์์ ํ๋ฆ์ผ๋ก ๊ณต์ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ฉด์ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ฌํ ๋ถ๋ถ์์ ์ด๋ค ๋ฌธ์ ๋ฅผ ์ฐ๋ฆฌ๊ฐ ๋ง์ฃผํ ์ ์๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์๋์ง ์์๋ณด๋ ์๊ฐ์ ๊ฐ์ ธ๋ณด์.
ํด๋น ๊ธ์ ์ฌ๋ด ๋ฐํ์ ์ถ๊ฐ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฑํ์์ต๋๋ค.
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์์ ์ผ์ด๋๋์ง ์ ์ ์๋ค.