티스토리 뷰

iOS

iOS ) GCD - Dispatch Queue사용법 (1)

Zedd0202 2018. 4. 29. 15:33
반응형

안녕하세요 :) Zedd입니다.

ㅂㄷㅂㄷ 다시 쓰겠음 <Concurrency Programming Guide - Dispatch queue>의 앞부분을 읽고오시면 도움이 될 것 같아요. GCD에 대한 개념?은 건너뛰고 사용법만 볼거라..그리고 글에서 문서를 그만읽는다고 했었는데, 문서에 좋은 내용있으면 추가하도록 할게요!



GCD - Dispatch Queue




자 여러분 일단 Queue가 뭐죠?  

<Concurrency Programming Guide - Operation queue>

<Concurrency Programming Guide - Dispatch queue>


글에서 계~~~속 queue가 나왔는데, queue는 선입선출! 즉 FIFO가 특징이죠? 먼저 온 사람이 먼저 서비스를 받는 그런 자료구조에요.


이 queue에는 두가지 종류가 있었죠. 바로

Serial

Concurrent

였습니다.


Serial은 다들 아시다 시피, 하나의 작업이 끝나고 그 작업이 끝나야만! 다음 작업을 시작하는? 한번에 하나의 작업만 하는 그런 queue였죠.

그리고 concurrent는 한번에 하나의 작업만 하고 그런게 아니라!!!!! 동시에!!!동시에!!!! 여러 작업들을 시작하는 거죠.

하지만!!!!!!!!!!!!!!!!!!!!!! 동시에 여러작업을 한다고 해서 막 니가먼저 내가먼저 이런게 아니라, 일단은  queue이기 때문에



출처 : https://www.raywenderlich.com/148513/grand-central-dispatch-tutorial-swift-3-part-1


먼저 들어온 작업이 먼저 서비스를 받는 것입니다. 근데 그걸 동시에 하는것 뿐이에요. 


자 이 queue가 Serial과 Concurrent로 나눌 수? 있다고 했는데 이 Serial과 Concurrent의 특징이 Operation queue에 들어갔던 것처럼 Dispatch Queue에도 들어갈 수 있습니다. 

그러니까 Dispatch Queue에는 Serial Dispatch Queue와 Concurrent Dispatch Queue가 있겠네요. 

Serial Dispatch Queue에 task들을 넣으면 한 task가 끝나야지 다음 task를 하겠고,

Concurrent Dispatch Queue에 task들을 넣으면 앞 task의 완료 여부에 상관없이 동시에 task를 실행하겠네요.

그럼 우리는 Serial 또는 Concurrent



근데!!!!!!!!!!! Cocoa Application은 미리 만들어진 queue 2가지를 제공합니다. 바로..우리가 많이 보던 main과 global인데요.


main dispatch queue는 <Concurrency Programming Guide - Dispatch queue>글에서도 언급했죠?

main dispatch queue는 앱의 main 쓰레드에서 task를 실행하는, 전역적으로 사용 가능한 serial queue입니다. 이 큐는 앱의 실행루프와 함께 작동하여 큐에 있는 task의 실행을 실행루프에 연결된 다른 이벤트 소스의 실행과 얽힙니다. 앱의 main쓰레드에서 실행되므로, main queue는 종종 앱의 주요 동기화 지점으로 사용됩니다. main dispatch queue를 만들 필요는 없지만, 앱이 적절하게 배수(drains)되도록 해야합니다. 

일단 main dispatch queue는 Serial queue라고 하니까..한 task가 끝나야지 다음 task를 할 수 있겠네요.

그리고 보통 찾아보면 반드시 이 main queue에 UI와 관련된 task를 넣어야 한다고 그러죠. 

왜 그런걸까요?!?!?

저는 이런게 문서에 나오고 그러니까 읽는건데 이번엔 안나옴 ㅎ

아무튼 따로 정리했습니다 <왜 main.sync를 하면 안될까>


그리고 나머지 하나는 global queue죠. 

<Concurrency Programming Guide - Dispatch queue>글에서 역시 언급했습니당 Concurrent queues(일종의 global dispatch queue라고도 알려진)은 동시에 하나 이상의 task를 실행하지만 task는 큐에 추가된 순서대로 게속 시작됩니다. 현재 실행중인 task는 dispatch queue에서 관리하는 고유한 쓰레드에서 실행됩니다.


그러면 써봅시다.


DispatchQueue.main.sync {

    //code

}

DispatchQueue.global().async {

    //code

}


많이들 보셨고 작성하셨을 코드지만..(아 물론 main.sync은 deadlock를 일으키므로 절대 main에서 sync를 호출하면 안됩니다.)

sync, async는 말그대로 동기적으로 처리할거냐 비동기적으로 처리할거냐 입니다.  main이든 global이든 sync, async둘 다 가지고 있어요.

sync, async는 잠깐 나중에 보기로 하고,


DIspatchQueue.main은 그냥 main이라 적어줬는데, global은 아니네요. main은 타입프로퍼티인가봐요.



main은 역시 타입프로퍼티군여ㅛ. 하지만 global은 사실 파라미터로 무언가를 받네요!

근데 우리가 안넣어줘도 되는 이유는? default로 값이 뭔가 주어졌겠죠?

그렇습니다...!

자 그럼 이 파라미터로 들어가는 qos를 알아야 하는데,

qos는 (quality of service)로, 음.. 중요도라고 생각하시면 됩니다. DispatchQoS overview 잠깐 볼까요?

QoS (Quality of Service) class DispatchQueue에서 수행  작업을 분류합니다. (

DispatchQoS “클래스라는게 아니라, (구조체입니다.) DispatchQoS qosClass라는게 있어요 qos 파라미터로 들어간건 DispatchQoS.QoSClass라는 enum입니다.)

작동시킬 QoS를 지정함으로써 중요도를 표시하고, 시스템이 우선순위를 정하고 이에따라 스케쥴링을 정합니다. 우선순위가 높은 작업은 우선순위가 낮은 작업보다 더 빨리 수행되고, 리소스가 많으므로 일반적으로 우선순위가 낮은 작업보다 더 많은 에너지가 필요합니다. 앱이 수행하는 작업에 적합한 QoS클래스를 정확하게 지정하면, 앱이 반응적?(responsive)이고 에너지 효율이 좋다는 것을 보장 할 수 있습니다.
오...
맨날 그냥 global()이라고 쓴 나...반성......ㅎㅎ..........
그럼 이 class들에는 뭐가 있는지 봅시다.




음...여기에 QoS정리하면 좋을텐데..이건 다른 글에 쓰고싶어서..! 패스 하핳 적으면 링크 남겨 놓을게요!

=> http://zeddios.tistory.com/521



DispatchQueue.global(qos: .background).async {

    //code

 

}


아무튼 이렇게 쓸 수 있으며, 적절하게 QoS를 지정해주면 앱의 에너지 효율이 좋아진다는 것!!!!! 아무것도 안적고 그냥 global()로 하면 default인건 위에서 말했죠?


자, 이때까지 main과 global을 이야기 했는데, 나만의 큐를 만들 수도 있습니다!!!!!!!! 두둥


let zeddQueue = DispatchQueue(label: "zedd")


바로 이렇게 말이죠. 


convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)


DispatchQueue의 생성자는 원래 위처럼 생겼어요.  label을 제외한 나머지 값들이 default로 지정되어있으니, 따로 추가를 안해줘도 되는것이죠.

중요하게 봐야 할건 3번째 파라미터인 attribute인데요, (다른것도 중요함)


let zeddQueue = DispatchQueue(label: "zedd")


이렇게 attribute를 주지 않고 그냥 만들면 이 큐는 Serial Queue가 됩니다.


let zeddQueue = DispatchQueue(label: "zedd", attributes: .concurrent)


이렇게 명시적으로 지정을 해줘야!!!!! 비로소 concurrent queue가 됩니다.



자 그럼 sync, async이야기를 해볼까요.

sync는 자, 제가 큐에 작업을 추가했어요 그럼 그게 끝날때까지 “기다리는” 겁니다.

async는 일단 그냥 큐에 작업을 추가하고, 나는 그냥 뭐 기다리는것도 없고 그냥 나는 다른 작업 할 수 있는 거에요.

출처 : https://www.quora.com/What-are-the-differences-between-parallel-concurrent-and-asynchronous-programming

내가 세탁기 돌려놓고 책읽는 것처럼.. 이게 바로 async입니다. 


그럼 일단 queue는 sync, async 둘 중에 하나로 작업을 돌릴 수 있겠네요. 중첩도 가능하지만 일단 하나만 있는걸 봅시다.

일단 편하게 sync부터 봅시다.


DispatchQueue.global().sync {

    for i in 1...5 {

        print(i)

    }

    print("==================")

}

for i in 100...105 {

    print(i)

}



자. global로 만들고 sync로 만들었으니, 이 global큐가 끝날때 까지는 다른 작업을 하지 않겠네요.





이는 Serial queue도 마찬가지.


let zeddQueue = DispatchQueue(label: "zedd")

zeddQueue.sync {

    for i in 1...5 {

        print(i)

    }

    print("==================")

}


for i in 100...105 {

    print(i)

    

}


결과는 위와 동일.


그렇다면 async를 해보자. 먼저 global.


DispatchQueue.global().async {

    for i in 1...5 {

        print("\(i)🐶")

    }

    print("==================")

}

DispatchQueue.global().async {

    for i in 200...205 {

        print("\(i)😍")

    }

    print("==================")

}


for i in 100...105 {

    print("\(i)👻")

    

}



(구분이 안가서;; emoji붙힘)




결과는 매번 실행때 마다 다릅니다. 왜냐면 async는 완료 여부에 상관없이 다음 코드를 실행하니까..하지만 global. 즉 concurrent기 때문에 개(🐶)가 끝나지 않아도 😍가 실행될 수 있죠. 왜냐? concurrent니까..동시에...실행하니까...

하지만 Serial에 async를 해보겠습니다.


let zeddQueue = DispatchQueue(label: "zedd")


zeddQueue.async {

    for i in 1...5 {

        print("\(i)🐶")

    }

    print("==================")

}

zeddQueue.async {

    for i in 200...205 {

        print("\(i)😍")

    }

    print("==================")

}


for i in 100...105 {

    print("\(i)👻")

    

}


역시 async기 때문에 결과는 매번 다르지만..



한가지는 확실하죠. 개🐶가 끝나야만 😍가 실행됩니다. 왜냐????? Serial이니까.......한 task가 끝나야 다음 task가 실행되니까...유령👻은 큐안에 들어가 있지 않으니 언제 실행될지 모름 ㅎ


Dispatch WorkItem

WorkItem은 별거 아니고, 수행 할 수 있는 task를 캡슐화 한거에요. 이 WorkItem은 Dispatch queue 및 Dispatch Group에 dispatch 할 수 있어요.

Dispatch WorkItem은 DispatchSource이벤트, 등록 또는 취소 handler로 설정 할 수도 있습니다.

어렵게 생각하지말거


let zeddWorkItem = DispatchWorkItem {

    for i in 1...5 {

        print("Dispatch WorkItem \(i)")

    }

}

DispatchQueue.global().async(execute: zeddWorkItem)


이렇게..말 그대로 정말 캡슐화....



반응형