안녕하세요. 후르륵짭짭입니다.
이번에도 글을 하나 올리려는데, Thread를 처리할 일이 있어서
공부하다 보니 헷갈리는게 있어서 글을 작성하려고 합니다.
** Serial Queue에서 Async는 무슨 의미가 있을까 **
DispatchQueue에서 Serial과 concurrent가 있는데,
Serial에서 Async를 하더라도 들어온 순서대로 작업하게 되어 있다면
Async가 어떤 효과가 있는지 감이 안 잡혔다.
let queue = DispatchQueue(label: "my.concurrent.lock.queue")
DispatchQueue.global().async {
queue.async {
print("async - 1 \(Thread.current)")
for i in 20..<30 {
print("⚪️", i)
}
}
queue.async {
print("async - 2\(Thread.current)")
for i in 101..<120 {
print("🔷", i)
}
}
queue.sync {
print("sync \(Thread.current)")
for i in 0..<10 {
print("🤮", i)
}
}
}
//결과
async - 1 <NSThread: 0x600002e40600>{number = 5, name = (null)}
⚪️ 20
⚪️ 21
⚪️ 22
⚪️ 23
⚪️ 24
⚪️ 25
⚪️ 26
⚪️ 27
⚪️ 28
⚪️ 29
async - 2<NSThread: 0x600002e40600>{number = 5, name = (null)}
🔷 101
🔷 102
🔷 103
🔷 104
🔷 105
🔷 106
🔷 107
🔷 108
🔷 109
🔷 110
🔷 111
🔷 112
🔷 113
🔷 114
🔷 115
🔷 116
🔷 117
🔷 118
🔷 119
sync <NSThread: 0x600002e50740>{number = 6, name = (null)}
🤮 0
🤮 1
🤮 2
🤮 3
🤮 4
🤮 5
🤮 6
🤮 7
🤮 8
🤮 9
결국에는 Serial이기 때문에 순차적으로 나오기 때문이다.
그런데, Main Thread 같은 경우는 Serial로 되어 있고
sync로 사용하면 모든 UI가 잡히게 되고
Async로 작업을 해야한다.
그럼 분면 Async의 특징이 있다는건데,,,,
** Sync Thread와 Normal Thread는 공유한다 **
Normal Thread는 우리가 일반적으로 사용하는 sync 작업(절차지향적인)을 의미한다.
Serial은 순차적으로 들어온 작업을 수행하는 것으로 알고 있다.
하지만 이는 같은 Queue에 들어 있을 때를 의미하는 것이다.
즉, 같은 Thread라고 하더라도 같은 Queue에 들어가 있지 않으면 영향을 받지 않는다는 것이다.
따라서 Normal Thread와 Async Thread는 같은 Queue에 없고 Thread도 다르기 때문에 영향을 받지 않는다.
let queue = DispatchQueue(label: "my.concurrent.lock.queue")
DispatchQueue.global().async {
print("HEAD \(Thread.current)")
queue.sync {
print("sync \(Thread.current)")
for i in 0..<10 {
print("🔷", i)
}
}
queue.async {
print("Sleep \(Thread.current)")
sleep(9)
print("Awake \(Thread.current)")
}
queue.async {
print("async - 1 \(Thread.current)")
for i in 20..<30 {
print("⚪️", i)
}
}
queue.async {
print("async - 2\(Thread.current)")
for i in 101..<110 {
print("🔷", i)
}
}
print("Finish \(Thread.current)")
}
//결과
HEAD <NSThread: 0x600002ba9940>{number = 6, name = (null)}
sync <NSThread: 0x600002ba9940>{number = 6, name = (null)}
🔷 0
🔷 1
🔷 2
🔷 3
🔷 4
🔷 5
🔷 6
🔷 7
🔷 8
🔷 9
Sleep <NSThread: 0x600002bb0a40>{number = 7, name = (null)}
Finish <NSThread: 0x600002ba9940>{number = 6, name = (null)}
Awake <NSThread: 0x600002bb0a40>{number = 7, name = (null)}
async - 1 <NSThread: 0x600002bb0a40>{number = 7, name = (null)}
⚪️ 20
⚪️ 21
⚪️ 22
⚪️ 23
⚪️ 24
⚪️ 25
⚪️ 26
⚪️ 27
⚪️ 28
⚪️ 29
async - 2<NSThread: 0x600002bb0a40>{number = 7, name = (null)}
🔷 101
🔷 102
🔷 103
🔷 104
🔷 105
🔷 106
🔷 107
🔷 108
🔷 109
하지만!
Normal Thread는 Sync Thread와 공유하기 때문에
Sync Thread가 Lock이 걸리면 기다리게 됩니다.
그래서 위와 같은 결과가 나오게 됩니다.
즉 , Sync를 통해 Thread 작업을 기다리고 Async에서 9초를 기다릴 때 다른 Thread 다른 Queue이기 때문에
Normal Queue는 기다리지 않고 작업을 하게 됩니다.
** 다른 쓰레드에서 작업하면 **
위와 같이 되어 있다면 어떻게 될까?
즉, 하나의 Custom Serial Queue에서 두개의 다른 쓰레드 Queue에서 작업을 하게 되면 어떻게 될까
위에 잡힌 개념 그대로 하면 된다.
1) 진한 초록색 - 싱크
2) 연한 초록색 - 싱크
3) 진한 하얀색 - 노말 싱크
3) 연한 하얀색 - 노말 싱크
4) 진한 누런색 -어싱크
5) 연한 누런색 - 어싱크
위의 순서대로 작업하게 된다 .
예시) - 순서는 프로세스가 어떻게 작업하는지에 따라 달라진다
let queue = DispatchQueue(label: "my.concurrent.lock.queue")
DispatchQueue.global().async {
print("HEAD - 2\(Thread.current)")
queue.sync {
print("sync - 2\(Thread.current)")
for i in 0..<5 {
print("🤖", i)
}
}
queue.sync {
print("sync - 2\(Thread.current)")
for i in 0..<10 {
print("🤮", i)
}
}
queue.async {
print("async - 2 \(Thread.current)")
for i in 0..<10 {
print("🤢", i)
}
}
print("END - 2 \(Thread.current)")
}
DispatchQueue.global().async {
print("HEAD - 1\(Thread.current)")
queue.sync {
print("sync \(Thread.current)")
for i in 0..<5 {
print("🔷", i)
}
sleep(1)
}
queue.async {
print("async - 1 \(Thread.current)")
for i in 20..<25 {
print("⚪️", i)
}
}
queue.async {
print("Sleep - 1 \(Thread.current)")
sleep(5)
print("Awake - 1 \(Thread.current)")
}
queue.async {
print("async - 1 \(Thread.current)")
for i in 20..<25 {
print("👻", i)
}
}
print("END - 1 \(Thread.current)")
}
//결과
HEAD - 1<NSThread: 0x60000374ce80>{number = 7, name = (null)}
HEAD - 2<NSThread: 0x600003741400>{number = 6, name = (null)}
sync <NSThread: 0x60000374ce80>{number = 7, name = (null)}
🔷 0
🔷 1
🔷 2
🔷 3
🔷 4
sync - 2<NSThread: 0x600003741400>{number = 6, name = (null)}
🤖 0
🤖 1
🤖 2
🤖 3
🤖 4
async - 1 <NSThread: 0x6000037503c0>{number = 3, name = (null)}
END - 1 <NSThread: 0x60000374ce80>{number = 7, name = (null)}
⚪️ 20
⚪️ 21
⚪️ 22
⚪️ 23
⚪️ 24
Sleep - 1 <NSThread: 0x6000037503c0>{number = 3, name = (null)}
Awake - 1 <NSThread: 0x6000037503c0>{number = 3, name = (null)}
async - 1 <NSThread: 0x6000037503c0>{number = 3, name = (null)}
👻 20
👻 21
👻 22
👻 23
👻 24
sync - 2<NSThread: 0x600003741400>{number = 6, name = (null)}
🤮 0
🤮 1
🤮 2
🤮 3
🤮 4
🤮 5
🤮 6
🤮 7
🤮 8
🤮 9
async - 2 <NSThread: 0x6000037503c0>{number = 3, name = (null)}
🤢 0
🤢 1
🤢 2
🤢 3
END - 2 <NSThread: 0x600003741400>{number = 6, name = (null)}
🤢 4
🤢 5
🤢 6
🤢 7
🤢 8
🤢 9
** Main Queue에서 Async를 사용해야하는 이유 **
그럼 이제 결국 Main Queue에서 Async를 사용하라는 이유를 알 수 있을 것이다.
우린 절차지향적인 방법을 할 때는 Normal Thread를 사용하고 있을 것이다.
그런 Normal Thread가 Serial에서 작업할 때 멈추면 안된다.
따라서 Normal Thread가 영향 받지 않으면서 안전하게 작업을 하기 위해서 Serial Queue에서
Async로 작업해야하는 것이다.
** Serial에서 Async에 Sync를 넣으면 **
Serial Queue의 Async작업에 Sync를 넣으면
안에 들어 있는 것이라서 Async 작업을 먼저 넣고 Sync작업을 넣게 됩니다.
let queue = DispatchQueue(label: "my.concurrent.lock.queue")
DispatchQueue.global().async {
print("HEAD \(Thread.current)")
queue.sync {
print("sync \(Thread.current)")
for i in 0..<10 {
print("🔷", i)
}
}
queue.async {
print("Sleep \(Thread.current)")
sleep(1)
queue.sync {
print("async \(Thread.current)")
for i in 0..<5 {
print("🤮", i)
}
}
print("Awake \(Thread.current)")
}
queue.async {
print("async - 1 \(Thread.current)")
for i in 20..<30 {
print("⚪️", i)
}
}
print("Finish \(Thread.current)")
}
//결과
HEAD <NSThread: 0x600002ec0a80>{number = 6, name = (null)}
sync <NSThread: 0x600002ec0a80>{number = 6, name = (null)}
🔷 0
🔷 1
🔷 2
🔷 3
🔷 4
🔷 5
🔷 6
🔷 7
🔷 8
🔷 9
Sleep <NSThread: 0x600002ec9400>{number = 5, name = (null)}
Finish <NSThread: 0x600002ec0a80>{number = 6, name = (null)}
즉 해당 코드와 위의 그림을 보면 Sync 작업을 가장 마지막에 Queue에 넣게 됩니다.
하지만 실제로 호출할 때는 첫번째 Async에서 호출하게 됩니다.
하지만 Sync 작업은 가장 맨 마지막에 있기 때문에 끝날 때 까지 기다리게 됩니다.
즉 DEAD LOCK에 걸려버린 상황 입니다 .
Sync 작업이 끝나야 Awak를 호출 할 수 있는데,
Sync 작업은 맨 마지막에 있으니 계속 기다리는 현상이 오는 겁니다.
** 참고 사이트 **
https://zeddios.tistory.com/519
https://babbab2.tistory.com/64?category=831129
'Xcode > Swift - PlayGround' 카테고리의 다른 글
PlayGround) RxTest에서 Timer들어간 Observable 테스트 (0) | 2022.04.24 |
---|---|
PlayGround) Subscript란? (0) | 2022.03.15 |
PlayGround) RxSwift-Error Handle (0) | 2022.02.07 |
PlayGround) Operation Queue - 2 (0) | 2022.01.17 |
PlayGround) Opeation Queue - 1 (0) | 2022.01.17 |
댓글