-
GCD, DispatchiOS 2021. 1. 19. 13:37728x90
개인적으로 공부하며 정리하는 블로그 입니다.
오류나 부족한 부분이 있을 수 있으니 감안하여 봐주시고 아낌없는 조언 감사드립니다 :D[한 줄 요약]
GCD - 작업을 병렬적으로 처리하기 위해 애플이 제공해주는 API.
C기반 메커니즘의 저수준 API로 스레드를 생성하고 할당하는 등의 스레드 관리를 대신해준다.
# sync vs async, Queue에 작업을 추가하는 방식
sync - 작업을 추가하고 작업이 끝날 때까지 기다린다.
async - 작업을 추가하고 바로 넘어간다.
# serial vs concurrent, Queue 안의 작업을 처리하는 방식
serial - 작업을 하나씩 꺼내서 처리한다.
concurrent - 작업을 꺼낸 뒤 기다리지 않고 바로 이어서 다음 작업을 꺼낸다.
* main 큐는 serial 큐, global 큐는 concurrent 큐흔히 GCD라고 부르는 Grand Central Dispatch에 대해 정리해보려고 합니다. GCD는 앱개발을 하다보면 매우 중요한 내용이고, 관련 글도 굉장히 많은데요. 면접에서도 단골 질문이죠! 근데 막상 GCD가 뭐야? 라고 물어보면 한 마디로 정의내리지 못하는 경우가 더러 있는 것 같아요. (네 저요...)
오늘의 목표는 사용법 보다도 "GCD가 뭐야?" 라는 질문에 "깔끔하게 답하기" 입니다.
당연히 serial, concurrent, sync, async도 헷갈리면 안 되겠죠?? 😀
GCD란?
Dispatch
Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.
시스템이 관리하는 dispatch queue에 작업을 보내서 멀티코어 하드웨어에서 코드를 병렬적으로 실행하세요.먼저 GCD가 관련 글을 읽다보면 DispatchQueue부터 설명하는 경우가 있는데요,
그럼 GCD == DispatchQueue 일까요? DispatchQueue는 GCD에서 사용하는 대표적인 객체지만, 엄밀히 따지면 GCD가 DispatchQueue라는 건 반쪽짜리 설명이 될 것 같아요. GCD 방식을 사용한 기술? GCD로 구현된 object 클래스? 정도라고 할 수 있을 것 같아요!Dispatch framwork 문서를 보면
"Dispatch, also known as Grand Central Dispatch (GCD)," 라고 나와있는데요, 이처럼 Dispatch를 GCD 라고도 부른다고 하네요.
위 Dispatch의 설명처럼 일할 수 있는 코어가 여러 개 있을 때 작업을 적절히 분배하는 일을 dispatch queue가 알아서 처리해주겠다는겁니다. GCD는 C기반 메커니즘으로 저수준 API입니다. 스레드를 생성하고 할당하는 관리를 시스템에게 맡깁니다. 개발자가 할 일은 DispatchQueue에 작업을 넣어주기만 하면 됩니다 :) (애플 짱)추가로 GCD가 아닌 다른 방법으로 Operation queues를 이용하는 방법도 있는데요, 이것도 DispatchQueue 처럼 작업을 멀티 스레딩으로 알아서 처리해주는 건 같지만, Objective-C 기반으로 GCD보다는 한 단계 더 추상화 된 API입니다. KVO 옵저빙, 작업 취소 등 GCD보다 더 많은 작업을 할 수 있지만 그만큼 오버헤드가 더 있습니다. Operation Queues를 이용한 방법이 더 오래된 방법인데요, 이 작업을 가볍게 만든 게 GCD 라고 생각하시면 될 것 같습니다.
Dispatch Queues
GCD의 개념은 알았으니 좀 더 구체적으로 알아봐야겠죠? GCD가 하고자 했던 코드를 concurrently하게 실행시키는 작업을 DispatchQueue를 이용해서 할 수 있습니다. 기본적으로 기억할 점은 DispatchQueue는 FIFO 방식의 Queue라는 겁니다. 종류가 어찌되었던 먼저 들어온 작업을 먼저 시작합니다.
Serial vs Concurrent
DispatchQueue의 종류로는 Serial, Concurrent 와 serial 큐지만 매우 중요한 Main Dispatch Queue가 있습니다.
Serial queues는 큐 안의 여러 작업들을 하나씩 처리합니다. 그리고 각 작업은 다른 스레드에서 실행될 수 있습니다. 같은 serial queue 안에 들어있고, 하나씩 처리된다고 해서 꼭 한 스레드에서 처리되는 건 아니라는 의미입니다. DispatchQueue를 생성하면 기본적으로 serial queue로 생성됩니다.
// default serial queue let serial = DispatchQueue.init(label: "kr.woongs.serial")
Concurrent queues는 큐 안의 여러 작업들을 동시에 처리합니다. 먼저 말씀드렸다시피 DispatchQueue는 FIFO기 때문에 작업이 실행되는 순서는 보장됩니다. 대신 앞의 작업이 끝나길 기다리지 않고 바로 다음 작업을 다른 스레드에게 할당할 수 있습니다. 따라서 작업이 끝나는 시간은 보장할 수 없게됩니다. concurrent 큐로 만들려면 따로 attributes를 지정해줘야 합니다.
// concurrent로 만들려면 attributes 지정 let concurrent = DispatchQueue.init(label: "kr.woongs.concurrent", attributes: .concurrent)
Main dispatch queue는 메인 스레드의 작업을 처리하는 serial 큐입니다. 메인 run loop와 함께 동작합니다. 별도의 큐를 지정하지 않고 작성하는 코드는 이 main queue에서 처리됩니다.
Task, 작업이 뭔가요?
앞에서 '작업' 이라는 표현을 계속 사용했는데요, 정확히 이 작업은 어떤 단위일까요?
작업은 Queue에 할당하는 블락(클로저) 단위입니다.concurrent라고 하더라도 블락 안의 작업은 코드 단위로 실행된다는 말씀을 드리고 싶었어요!
concurrent 큐를 사용했지만, 아래 코드는 순서대로 출력이 됩니다 :)
concurrent.sync { for i in 0...5 { print(i) } print("======") for i in 10...15 { print(i) } }
concurrent란 이 코드 블럭들이 동시에 처리된다는 의미에요 :)
작업을 더 추가해볼까요? 보기좋게 개수를 좀 줄여볼게요.concurrent.async { for i in 0...3 { print(i) } print("======") for i in 10...13 { print(i) } } concurrent.async { for _ in 0...3 { print("🔥") } }
이 코드의 결과를 보면, 🔥출력 부분과 숫자 출력 부분이 섞여서 동시에 진행되는 걸 확인하실 수 있습니다! 역시 숫자는 순서대로 잘 나오구요!
seiral 이라면?
serial.async { for i in 0...3 { print(i) } print("======") for i in 10...13 { print(i) } } serial.async { for _ in 0...3 { print("🔥") } }
작업 순서대로 잘 나오네요 :)
sync vs async
이제 작업 단위는 헷갈리지 않으시겠죠? 앞서 살펴본 serial이냐 concurreny냐는 큐 안의 작업을 어떻게 처리할건지에 대한 내용이었다면,
다음으로는 큐에 작업을 추가하는 방식인 synchronous 와 asynchronous 에 대해 알아보겠습니다.sync는 작업을 추가하고 작업이 다 처리될 때까지 다른 일은 하지 않고 기다리겠다는 의미에요.
아까 concurrent 큐의 작업 실행을 sync로 바꿔볼까요?
concurrent.sync { for i in 0...3 { print(i) } print("======") for i in 10...13 { print(i) } } print(">>> next task") concurrent.sync { for _ in 0...3 { print("🔥") } }
분명 concurrent 큐인데 마치 serial 큐처럼 결과가 나왔네요!! 이유는 sync로 실행했기 때문에 숫자를 다 찍을 때까지 다음으로 넘어가지 않고 기다리고 있기 때문이에요!! 큐 사이에 프린트를 하나 해봤는데요, sync면 아예 다음 코드라인을 읽지 않고 기다린다는 걸 확인할 수 있습니다 :)
async는 작업을 추가하고 작업이 처리되길 기다리지 않고 바로 넘어가겠다는 의미입니다. 이미 앞선 예제에서 확인해볼 수 있었죠? 오해하시면 안될 게 sync라고 해서 아예 그 작업만 하고 다른 건 하지 않겠다는 게 아니에요! 이 작업이 끝날 때까지 기다린다는거죠! 따라서 먼저 작업 중이던 일은 열심히 일한답니다.
sync 앞에 async 작업이 있다면??
concurrent.async { for _ in 0...5 { print("🐳🐳") } } concurrent.sync { for i in 0...3 { print(i) } print("======") for i in 10...13 { print(i) } } concurrent.sync { for _ in 0...3 { print("🔥") } }
네 놀지않고 고래를 잘 찍네요 :)
다른 큐의 작업은??
serial.async { for _ in 0...3 { print("🐳") } } serial.async { for _ in 0...3 { print("🐳🐳") } } concurrent.sync { for i in 0...3 { print(i) } print("======") for i in 10...13 { print(i) } } concurrent.sync { for _ in 0...3 { print("🔥") } }
네 역시 다른 큐도 열심히 일하고 있네요 :))
GCD에 대해 알아봤습니다. 헷갈리고 두루뭉실 했던 개념들이 있으셨다면 정리하는 데 조금이라도 도움이 되셨으면 좋겠네요!
읽어주셔서 감사합니다 :)
참고
https://developer.apple.com/documentation/DISPATCH
https://github.com/apple/swift-corelibs-libdispatch
https://apple.github.io/swift-corelibs-libdispatch/
'iOS' 카테고리의 다른 글
CALayer 그리고 View와의 관계 (0) 2021.01.31 Intrinsic Content Size, Content Hugging, Content Compression Resistance (0) 2021.01.24 [개발자 문서읽기] UIApplicationMain(::::) (0) 2021.01.16 [개발자 문서읽기] Responding to the Launch of Your App (0) 2021.01.16 [개발자 문서읽기] UIApplication (0) 2021.01.16