[WWDC16] Concurrent Programming with GCD in Swift3(1)

Youth·2024년 1월 30일

WWDC

목록 보기
1/4

안녕하세요:)
WWDC포스팅으로 찾아뵙게된 킴스캐슬입니다
요즘 GCD에대한 공부를 하고있는데 실제동작원리에대해서 알고있으면 코드나 실행시퀀스를 이해하는데 도움이되지않을까하는 마음에 구글링을 하다가 2016년 WWDC영상중에 GCD관련영상이있어서 정리를 해보게 되었습니다

WWDC영상이 예전에는 전부다 있었던거같은데 어느순간보니까 없어진 영상들도 많아졌고 16년도이전영상은또 볼수가없더라고요...너무 예전 기술이라고 생각해서 내린건지는 잘 모르겠네요

아무튼 16년도 영상에도 참 유명하고 공부하기 좋은영상들이 많습니다
그 유명한 Understanding Swift Performance도 16년도 영상입니다
몰랐는데 Protocol and Value Oriented Programming in UIKit Apps이라는 영상도 있더라고요

보니까 실제 app을 만들때 POP를 사용하는 예제를 설명해주는거같은데(아직 안봐서 모르지만 제목에서부터 그런느낌이 들지 않나요?)이 영상도 한번쯤 보고싶다는 생각이들더라고요

그래서 매일은 아니지만 일주일에 3일정도는 아침시간을 내서 년도순서대로 중요해보이는 영상을 보고 정리해보는 시간을 가져보려합니다

물론 노력은하겠지만 영상중간중간에 아무리 이해를하려해도 못하겠는부분이나 좀 너무딥해서 학습범위를 벗어난다싶은부분이있으면 스스로의 판단하게 넣지않고 꼭 필요하고 알아야할 부분만 빼서 정리를 해보려고 합니다

자 그러면 시작해보겠습니다:)

Introducing the idea of concurrency

이번 챕터의 제목은 동시성아이디어의 도입입니다
app을 만들게되면 사용자는 화면을 보게되죠 그리고 그 화면내에서 다양한 interaction을 수행합니다
버튼을 누르거나 스크롤을하거나하는 UI작업을 수행하게되고 그 결과를 유저가 보게됩니다

app이 실행되면 그 앱은 main thread를 얻게됩니다
main thread는 사용자 인터페이스를 구동하는 모든 코드를 실행하는 역할을 담당하게됩니다

근데 앱을 만들다보면 앱이하는일이 단순히 유저의 터치를 감지하거나 스크롤을감지해서 보여주는 역할만 하지는 않습니다 가끔은 이미지를 다운로드받아야할수도있고 혹은 어떤 큰규모의 처리를 해야할수도있습니다(이걸 여기서는 data transform이라고 이야기하고 상대적으로 오랜시간이걸리는작업을 통틀어서 이야기하는것같습니다)

main thread에서 그런 일들까지 하게되면 mac에서는 위 이미지의 오른쪽아래처럼 로딩뷰가 발생할수있지만 iOS에서는 UI가 버벅거리거나 최악의경우엔 화면이 멈춰버릴수도있습니다(유저는 단순히 화면이 멈춘게 아니라 앱이 멈춘거라고 생각하겠죠 어떤 생각을하던 최악의 UX로 기억에 남을겁니다)

이런 문제를 해결하는 방법의 시작으로 WWDC에서는 동시성 아이디어 도입을 제안합니다
그리고 iOS에서는 thread를 생성해서 동시성을 달성한다고 합니다

(thread는 동시에 코드를 실행할수있게 해준다고하네요)

방금말했던 Data transform같은 동작을 main thread가 아닌 새로생성한 thread에서 작업하게 해서 동시에 작업이 진행되면서도 main thread에서 data trasform작업으로 인한 성능저하를 예방할 수 있게되는거죠

그러면 여기까지 들었을때는 이런생각이들 수 있습니다

아 그럼 우리가 thread를 새로 생성해서 오래걸리는 작업을 넣어주면 되겠네?

하지만 그 작업이 쉬운작업은 아닙니다

왜냐면 CPU의 core는 한번에 하나의 thread밖에 동작시킬수없는데 thread를 늘리면 동시성이라는 아이디어를 도입할수있긴하지만 이로인해 코드의 불변성을 유지하는데 어려움이 생길수밖에 없습니다. 왜냐면 하나의 코드를 여러 thread에서 접근하게되면 thread safe하지 않을수있기때문이겠죠?

즉, 우리가 직접 동시성을 위해서 thread를 만들게되면 thread safe에대한 고려 코드의 불변성에대한 고려를 100% 완벽하게 해내기가 어려워지고 여러 문제를 야기할수있습니다(오히려 더 느려지는 결과를 마주하게될수도 있다고 하네요)

그렇게해서 apple에서 이런 동시성을 위해 만들어진 라이브러리가 GCD입니다

Grand Central Dispatch(GCD)

GCD는 iOS뿐아니라 apple기기 전반적으로 적용가능한 동시성 라이브러리라고 소개하고있습니다
GCD는 우리가 직접 thread를 만들지 않아도 자동으로 여러상황을 고려해서 안전하게 thread를 만들어주기위한 여러가지 기능을 제공해줄 수 있을겁니다. 이를 위해서 몇가지 추상화를 도입하게됩니다

몇가지 추상화를 도입한다는말이 조금 헷갈렸었는데 추상화라는 말에 너무 집중하기보다는 GCD를 사용하기위해서는 이런 메서드나 이런 방식을 사용해야해! 정도로 이해하고 넘어가시면 좋을것같습니다

이 몇가지 추상화중에 가장 중요한것이 Dispacth QueueRun Loop입니다

DispatchQueue와 Run Loop

DispatchQueue는 말그대로 Queue입니다 FIFO구조를 가지고 있는 자료구조이기도하죠
DispatchQueue는 해당 큐에 작업할 task를 넣을수있는 구조입니다

즉 dispatch queue에 내가 실행하고자하는 task를 넣죠 그리고 swift에서는 이 task를 클로저형태로 넣어주게됩니다

그래서 실행하고자하는 일을 클로저로 묶어서 dispatch queue에 넣기만하면 됩니다

여기서 중요한 부분이 dispatch queue가 하는 일은 단순히 클로저로 묶인 task를 내보내는 일이지 그 클로저내부의 뭔가를 실행하는일을 하지 않습니다 어떤 task를 실행하는일은 run loop가 하게됩니다

하지만 dispatchqueue에 task가 있어도 혹은 run loop가 task를 받아도 thread를 배정받지 않으면 아무일도할수가 없습니다

그래서 dispatchqueue에 어떤 task가 들어오면 그 task를 내보내는일을 하기위해서 GCD가 thread를 제공합니다 그러면 그때 비로소 dispatch queue에 task를 내보낼수있게되는거죠

중요한 포인트가 여기서 나오는데요 thread를 누구한데 줄지에 대한 결정권(thread제어권이라고도 말하는것같은데)을 GCD가 가지고 있습니다. 그래서 GCD에서는 thread제어권을 GCD가 가지고있는다라고 이야기를 하는것같네요

GCD에게 thread를 받은(=thread제어권을 받은) dispatch queue는 task를 다른곳으로 내보낼 수 있게됩니다

포스팅 초반에 main thread에대해서도 이야기를 했는데 main thread는 조금 특별하다고합니다
다른 thread의 경우에 queue와 run loop가 별도의 thread로 나눠져있던것에 비해서 main thread 내부에는 main run loop와 main queue가 동시에 존재하는 구조로 되어있습니다

정리를 해보면 이렇습니다

  1. task를 dispatchqueue에서 꺼내거나 꺼낸 task를 실행하려면 thread를 받아야한다
  2. thread를 받는다는것의 의미는 thread제어권을 받는다는 의미와 일맥상통하다
  3. thread제어권을 주는 주체는 GCD이다
  4. dispatch queue에서 task를 다 꺼내거나 run loop에서 task가 끝나면 thread제어권이 GCD에 의해 회수된다

Dispatch Queue의 two main way

dispatch queue에 작업을 전달할수있는 두가지 방법에대해 소개하는 챕터입니다
크게 asynchronous한 방법synchronous한 방법두가지가 있는데 비동기적인방법, 동기적인방법입니다

첫번째, 비동기적인 방법

dispatch queue에 비동기적으로 task를 보내면 dispatch queue입장에서는 내보내야하니까 thread제어권이 필요할거고 GCD가 그걸 알아채고 thread제어권을 주게될겁니다

이런상황이 될거죠 그러면 해당 thread에서 task 3개를 내보내기만할겁니다 내보낸후에는 어떤일이일어나던간에 신경쓰지 않습니다 이걸 비동기적으로 보낸다고 표현합니다

많은 강의에서 비동기라고 하면 어떤 작업을 기다리지 않는다라고 하는데 그 의미를 위의 그림에서 생각을 해보면 첫번째 task를 보낸후에도 thread제어권이 사라지지 않기때문에 첫번째 task가 끝날지 안끝날지와 상관없이 바로다음일(두번째 task를 내보내는일)을 하게됩니다. 즉, 어떤 작업을 기다리지 않는다라는 말이 thread제어권을 계속 가지고 있는다라는 말과 동일하다고 이해할 수 있습니다

그리고 이 task를 받은 run loop들을 GCD로부터 thread제어권을 받아 말그대로 동시적으로 task를 실행할 수있게될겁니다. 결국 run loop의 thread제어권을 주는 주체도 GCD이기에 GCD는 thread제어권을 GCD가 관리한다는 말을 할수있게됩니다

두번째, 동기적인 방법

dispatch queue에 동기적으로 작업을 보내면 어떻게될까요
이번에는 비동기적으로 작업을 보낸 thread(왜냐면 어쨌든 동기적으로 작업을 보낸 행위를 했다는건 thread에대한 제어권이있다는거니까요)와 dispatchqueue에 비동기적작업과 동기적인 작업이 들어갔고 이들을 처리하기위해서 GCD로부터 thread제어권을 받았다고 해보겠습니다

(화살표로 이어진 녀석이 dispatch queue에 보낸 동기적 작업입니다)

동기적으로 작업을 보내게되면 그 순간 해당 thread는 block이됩니다

block된다는 말은 그 thread를 사용할수없게되고 thread에대한 제어권이 없는상태라고도 볼 수 있습니다

하지만 dispatch queue입장에선 thread제어권을 받았으니 task들을 내보내기시작합니다 그러다가 동기적으로 보내진 두번째 task를 만나는 순간 dispatch queue가 thread제어권을 그 동기적 작업을 보낸 thread에게 전달해줍니다(중요한건 이 상황에서는 GCD가 thread제어권을 넘겨주는게 아니라 dispatch queue가 넘겨줍니다)

그러면 동기적작업을 보낸 thread가 제어권을 받았으니 그 동기적작업을 실행할수있게되겠죠
작업을 마치면 이번엔 GCD가 DispatchQueue의 제어권을 기존 thread로 반환해줍니다

이렇게 되면 동기적으로 작업을 보낸 thread입장에서는 그 작업이 실행될때까지 아무것도못함 + 그 작업이실행되는 동안 다른작업을 못함이기때문에 결론적으로는 해당작업이 끝난 후에야 다른작업을 할수있다고 할수있습니다(결국 같은말인거죠)

동기적이라는 개념에대해 설명한 다른 강의나 글들을 보면 작업이 끝날때까지 기다린다혹은 작업이끝난후에야 thread제어권을 받는다라는 말이 이런관점에서 나온 설명들이라고 할 수 있습니다

iOS에서의 동기적작업의 thread제어권을 받는시점이 보통은 작업이끝나고라고 하지만 WWDC영상을 기준으로 thread제어권을 받는시점은 보면 실제로 그 동기적작업이 시작할때인것같습니다 하지만 다른작업을 시작하기위한 thread제어권(run loop자체의 제어권)은 동기적작업이 끝나고 GCD가 알아서 줄것이기때문에 그런 관점에서는 작업이끝날때까지 block되었다가 thread제어권을 받는다라는 말이 맞는것같습니다

Getting Work Off Your Main Thread

처음에 data transform이라는 오래걸리는 작업을 UI작업에 방해되지않게 처리하기위해서 동시성을 도입했고 그걸위한 라이브러리로 GCD를 활용해서 dispachqueue에 task를 클로저형태로 보내면 GCD가 알아서 thread를 관리해주고 동시적으로 task를 처리할수있다고 배웠습니다

그러면 data transform의 결과를 다시 main thread로 가져오는 방법에대해서 배워봅시다

설명드렸던대로 우리가 기존에 main thread에서 작업하던 transtorm작업을 동시적으로 처리를하기로했습니다

그래서 그결과 main thread가 아닌 다른 thread에서 transform의 결과로 어떤 Data가 만들어졌다고 해봅시다

그러면 이친구를 다시 main thread로 가져와서 UI로 보여줘야겠죠?
네트워크 통신을해서 image를 만들어서 받아왔는데 그걸 유저한테 안보여주면 아무의미가 없잖아요

간단하게 dispatch queue로 보내서 실행된 작업을 다시 main thread에 있는 main queue에 보내주면됩니다 물론 그과정에서 main thread로 보내기만하고 다른 작업을 하기위해서 async로 보내게됩니다
(sync로 보내면 main thread에서 imageview에 image를 넣는동안 해당 thread가 block되겠죠)

자, 우리는 지금까지 코드를 어떻게 작성하고 이를통해서 다른 thread에 task를 배치하는 방법에대해 배웠습니다

Controlling Concurrency

근데 이렇게 GCD를 이용해서 thread를 만들고 사용하는게 완전무결한 방식일까를 생각해보는 챕터입니다
GCD를 이용해서 thread를 만들어 동시성을 관리하는걸 쉽게 동시성관리(Controlling concurrency)라고 하겠습니다

우선 기본적으로 thread를 사용할수잇는 pool에는 한계가 존재합니다 물리적 thread갯수는 하드웨어적으로 정해져있고 소프트웨어적으로 thread를 만들수는 있으나 thread를 바꿀때마다 context switching이발생하게됩니다

이게 지금당장은 뭔지몰라도 아무튼 thread를 관리하는데는 비용이발생하는구나 정도로 알고 넘어가주시면됩니다

직관적으로 생각해도 어떤 thread가 block이되었는데 당장 작업을 해야하는 상황이면 thread를 가져오거나 만들어야겠죠 그래야 block된걸 무시하고 다른작업을 매끄럽게 진행할수있을테니까요

GCD는 코드를 정말 잘 끊임없이 매끄럽게 실행할수있도록 thread를 가져오고 만들어가면서 합리적인 동시성을 제공하기위해 노력하고있습니다

하지만 어떤 thread에 대한 결과를 사용해야하는 다른 thread가있다면? 하지만 그 thread가 block된상태라면? 아마 block된 thread가 많이지게될거고 임계수를 넘은 thread가 생성될수있습니다 그렇게되면 물리적 thread이 갯수를 아득히 넘어서는 소프트웨어 thread가 생기게될거고 이때발생하는 context switching의 비용과 오버헤드는 유의미한 성능저하를 만들어내게됩니다

그리고 이걸 Thread Explosion이라고 합니다


마무리

여기까지가 WWDC영상의 약 20%정도되는 분량입니다...ㅎㅎ
다음 포스팅은 Structuring Your Application부터 시작하게될텐데 Dispatch Group에대한이야기부터 시작되지않을까 싶습니다

아마 뒷부분의 내용은 제가 지금당장 이해하려고했을때 크게 도움될수있는 부분이 앞부분에비해는 아니라고생각되서 약 60%까지의 내용만 다룰예정인데 그렇게생각하면 이번포스팅은 절반정도의 내용을 정리한 글이라고 할 수있을것같습니다:-)

WWDC Concurrent Programming with GCD in Swift3의 정리는 다음포스팅에서 마무리하는걸로하고 오늘은 이만 물러가보도록하겠습니다
그럼 20000~

profile
AppleDeveloperAcademy@POSTECH 1기 수료, SOPT 32기 iOS파트 수료

0개의 댓글