본문 바로가기
Xcode/Swift - PlayGround

PlayGround) Serial에서 Async는 머지?

by 후르륵짭짭 2022. 2. 28.
728x90
반응형

안녕하세요. 후르륵짭짭입니다.

이번에도 글을 하나 올리려는데, 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://stackoverflow.com/questions/49249654/in-gcd-are-serial-queue-synchronous-by-asynchronous-operation-swift

 

In GCD are serial queue synchronous by asynchronous operation Swift

I am using a serial queue with QoS background let serialQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.background) Assigning one jobs synchronous, and two jobs asynchronous: func serial...

stackoverflow.com

https://zeddios.tistory.com/519

 

iOS ) 왜 main.sync를 하면 안될까

안녕하세요 :) Zedd입니다. 오늘은 왜!!! 왜 main.sync를 하면 안되는지 자세히 공부해볼게요. 그렇다고 main.sync를 무조건 하면 안된다는것도 아닙니다. 요 부분은 밑에서 다시 볼게요. main.sync를 하게

zeddios.tistory.com

https://babbab2.tistory.com/64?category=831129 

 

iOS) Sync vs Async / Serial vs Concurrent

안녕하세요:) 오늘은 전 편에 이어 네트워크와 관련된 포스팅 2탄입니다!!!! 1탄 Process vs Thread가 궁금하신 분은 보고 오시고 :) 이번 포스팅은 진짜 귀에 딱지 앉도록 들었던 Sync vs Async와 Serial vs Co

babbab2.tistory.com

 

728x90
반응형

댓글