본문 바로가기
Swift 개발 이야기

Dispatch Queue in Swift

by 방화동한량 2020. 8. 20.
728x90

굿데이 여러분 오랜만입니다

 

오늘은 Dispatch Queue 에 대해서 간단하게 정리해보려구 합니다

 

Dispatch Queue 는 Serial Queue 와 Concurrent Queue 가 있는데여

 

Serial Queue 는 말그대로 직렬로 해당 Queue 에 여러 작업이 있으면 먼저 실행된 작업이 종료될때까지 다른 작업들은 대기를 타는 친구구여

 

Concurrent Queue 는 병렬 Queue 이기 땜시 Serial 과는 달리 Queue 안에 들어가 있는 친구들이 '동시에' 작업을 합니다 물론 순서는 먼저온 친구가 먼저 시작하긴 하는데, 누가 먼저 끝날지는 몰라여 ㅎ;

 

일단 여기서 1차 정리

 

시리얼큐 -> 먼저 들어온 놈이 혼자 먼저 실행되고, 이게 끝나야 다음 놈이 실행될수있음

컨커런트큐 -> 먼저들어온 놈이 먼저 실행은 되는데, 다음 놈들도 뒤따라서 바로바로 실행됨, 동시에 작업이 실행되기 땜시 누가 먼저 끝날지 몰라!

 

참 쉽져?

 

이제 서위퍼터에서 제공해주는 기본적인 큐를 알아보면, 저희가 맨날 UI 조작할때 쓰는거 하나 있쥬?

 

바로 DispatchQueue.main 입니다. 이친구는 바로 시리얼큐에여

 

그리고 DispatchQueue.global() 이 있는데 ,이 친구는 컨커런트큐입니당

 

근데 main 같은 경우는 따로 줘야할 값이 없는데, global 같은 경우는 뒤에 뭔가를 넣을 수가 있잖아여?

 

바로 그것이 qos 인데,  Quality of Service 로 해당 큐의 중요도를 지정해주는 겁니다

 

기본은 default 이기 때문에 저희가 따로 뭐 안주고 global() 로 해도 잘 돌아가기는 합니다..

 

이 친구들은 enum 으로 구성되어 있어서 해당 큐의 중요도에 따라 지정해주면 되는데,

간단하게 보면 UI 나 즉각적으로 반응해야 하는 부분이 윗부분이구,

네트워크 작업과 같이 백그라운드에서 돌아야 하는 작업들은 background 로 지정해주면 된다고 편하게 생각하시면 될듯 합니당

 

그리고 이 두가지 제공해주는 큐 말고 저희가 직접 만들수도 있는데염

let customQueue = DispatchQueue(label: "custom") // Serial Queue
let customConcurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent) // Concurrent Queue

label 과 attributes 를 지정해주면 아주 간단하게 커스텀 큐를 만들 수 있게 됨미다

 

자 이제 sync 와 async 에 대해서 알아보게씀니다

 

sync 는 큐 내부에서 해당 작업이 완료 될때까지 '기다리는' 것이고,

async 는 해당 작업과 동시에 다른 작업들도 '함께' 처리가 되는 것이라고 생각하시면 맘이 편합니당

 

시리얼큐와 컨커런트큐에서 이 두 친구들이 어떻게 작동되는지 알아보게씀니다.

 

먼저 컨커런트부터 알아볼게여 with global 큐큐

 

        DispatchQueue.global().sync {
            for i in 0...5 {
                print(i, "%")
            }
            print("VVVVVVVVVV")
        }
        
        DispatchQueue.global().sync {
            for i in 6...10 {
                print(i, "$")
            }
            print("QQQQQQQQQQ")
        }
        
        for i in 11...15 {
            print(i, "^")
        }
        
        print("XXXXXXX")

 

자 이렇게 되면 0...5의 작업이 먼저 실행되고 끝날때까지 암것도 못합니다.

 

그 다음 큐에 들어가있는 6...10 이 출력이 되고 난 후에야 큐 안에 들어가있지 않은 친구 11...15 가 출력이 되겠죵?

 

하지만 global queue 를 async 로 하게 되면?

 

아주 난리가 납니다. 순서고 뭐고 없이 출력이 되는데여 바로 async 하게 queue 안의 작업이 끝나지 않아도 다른 작업을 실행하기 때문입니다.

 

자 그러면 이제 serial queue 를 테스트해봅시당

 

이번엔 async 부터 해볼께여

 

        let queue = DispatchQueue(label: "custom")
        
       queue.async {
            for i in 0...50 {
                print(i, "%")
            }
            print("VVVVVVVVVV")
        }
        
       queue.async {
            for i in 51...100 {
                print(i, "$")
            }
            print("QQQQQQQQQQ")
        }
        
        for i in 101...150 {
            print(i, "^")
        }
        
        print("XXXXXXX")

 

커스텀 시리얼큐를 만들고 돌리면,

 

101...150 은 async 이기 때문에 호출되는 순서를 알순 없게 됩니다.

 

하지만 저희가 확신할 수 있는건? 시리얼큐 안에 들어가 있는 0...50 과 51...100 은 첫번째 작업이 끝나야만 출력이 된다는 점이져

 

sync 는 뭐 똑같습니다 queue 안에 있는 두 작업들이 완료가 되어야만 101...150 이 출력이 되는 것이져

 

그런데 왜 저 위에서는 컨커런트 큐 테스트할때 제공해주는 global() 을 썼는데,

 

시리얼큐 테스트때는 main 을 안쓴것일까 궁금해하시는 분이 혹시 있으실거 같아서 첨언을 드립니다

 

바로 main 을 sync 로 호출하게 되면 생기는 문제 때문인데여,,

 

main Queue 를 sync 로 호출하게 되면 아래와같은 오류메시지와 함께 앱이 터지게 됩니다

 

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

 

이는 main Thread 가 Thread-safe 하지 않기 때문이라고,,,합니다,,, 자세한 사항은 아래 제드님의 글이 있으니 참고하시면 될것 같아여

 

zeddios.tistory.com/519?category=682195

 

그리고 main queue 의 경우에는 다른 serial queue 와는 달리 async 를 해도 queue 밖에 있는 다른 친구들이 중간에 들어가질 않더라구여

 

이것도 아마 다른 serial queue 와는 다른 main queue 만의 특성이 아닌가,,,하는 생각을 해봅니다.

 

지금까지 아주 간단하게 dispatch queue 에 대해서 정리해보았는데여

 

혹시라도 이상한 부분이 있다면 편하게 지적해주시면 감사하게씀니다

 

그럼 다음에 또 만나요 안녕~