Weekoding

[Swift]DispatchQueue와 스레드의 개념 본문

공부 노트(Swift)

[Swift]DispatchQueue와 스레드의 개념

Weekoding 2022. 4. 25. 22:28

Xcode의 코드들을 접하다 보면 이런 코드를 보았을 것이다.

          DispatchQueue.main.async {
              self.tableView.reloadData()
          }

결론적으로는 'main thread에서의 비동기 실행을 위한 코드'이다.

어떤 상황에서 사용되며, DispatchQueue / .main / .async가 각각 무엇을 의미하는지 살펴보자.

또한 그 전에 프로세스, 스레드의 개념을 짚어볼 것이다.

 

 

⇢ 프로세스와 스레드

process: 실행중인 프로그램. '단위'로써의 개념을 갖고 있는 작업이라고도 할 수 있다.

보통의 프로그램들은 하나의 코드 덩어리로, 저장 장치에 저장되어 있는 정적인 상태이다.

그러나 실행되어 CPU를 할당받고 메모리에 올라가게 되면(동적인 상태가 되면) 작업으로써의 단위 개념을 갖게 되는 것이다.

 

thread: 프로세스 안에 포함되어 있는 실행 흐름의 단위.(= 실행 흐름의 최소 단위)

작업(process)은 당연히 코드 덩어리의 연산을 통해 이루어지는데, 연산은 연속적인 특성을 가진다.

이것은 하나의 흐름(flow 또는 context라고도 한다)을 만들게된다. 이것이 thread.

 

하나의 process는 최소 한 개의 thread를 가진다.

출처: 위키백과(스레드)

 

 

🧐  thread가 왜 필요할까?

프로그램의 복잡도가 높아짐에 따라 하나의 프로그램이 단순히 한 개의 작업만 하는 경우는 거의 없어졌다.

"그러면 한 개의 프로그램을 처리하기 위해 여러개의 process를 사용하면 되는 것 아닌가?": 불가능하다.

그 이유를 알기 전에 먼저 알아야할 개념이 몇가지 있다.

 

1. 관점에 따른 최소 단위

  CPU 관점 운영체제 관점
최소 작업 단위 thread process

운영체제는 각 process별로 할당된 메모리 내 정보에만 접근이 가능하도록 제약을 두고 있다.

 

 

2. 공동 영역

process는 운영체제로부터 작업에 쓰일 virtual memory를 할당받게 된다. 

이 단어들에 대해 비유를 해보자.

 

process: 가구(생활 단위)

virtual memory: 집(물리적)

thread: 구성원

 

재범, 성빈, 원재가 한 집에서 산다고 가정하면, 각자의 방이 있을 필요를 느낄 것이다.

이를 Thread local stroage라고 하며, stack구조로 관리된다.

그 외 거실, 주방, 화장실은 공용으로 사용하는데 무리가 없을 것이다. 이를 Code/Data/Heap이라고 부른다.

 

 

🤓  아하!

process는 서로 분리된 작업 영역을 갖고 있기 때문에 자원 공유가 어렵고(가능은 한데 자원 부담이 크다),

thread는 메모리를 공유하며 동작할 수 있기 때문에 thread라는 개념이 필요하고,

복잡한 process가 n개의 thread를 사용하여 작업을 하는 것이다.

이를 Multi-threading이라고 한다. (process가 여러개인 것은 Multi-tasking)

 

한 process에서 오류가 난 것이 다른 process에 오류를 주지는 않지만,

한 thread에서 문제가 발생하면, 공유 중인 메모리 영역이 있기 때문에 나머지 thread, 즉 process의 실행을 끝내버린다.

따라서 Multi-threading 환경에서는 동기화 관리가 필요하다.

( 공통된 자원을 여러 쓰레드에서 접근하는 경우 오류가 발생할 수 있기 때문이다. )

 

 

 

📂  iOS의 thread 

iOS에서 각 thread는 병렬적으로 동시에 실행이 가능하다.

또한 UIKit의 클래스들(기본 UI 관련 동작)은 모두 오직 main thread에서 실행된다.

  → Cocoa Touch앱에서, UIApplication의 인스턴스가 main thread에 붙기 때문이다.

 

허나 개발자가 작업 순서를 일일이 파악하여, thread에 골고루 분포하는 것은 매우 섬세하고 어려운 일이다.

( 작업을 어느 thread가 할지, 코어를 여러개 쓸지, Async로 처리할지, multi-thread로 할것인지 등... )

그래서 Swift는 GCD를 통해 multi-thread를 제어하는 기능을 제공한다.

 

1. GCD

GCD(Grand Central Dispatch API): iOS의 공유된 Thread pool을 관리하는 API.

GCD는 Dispatch queue들의 thread의 실행을 결정한다. 

DispatchQueue 명령어를 통해 동기, 비동기를 설정하여 GCD에 작업을 추가하면,

GCD가 시스템 안에서  작업에 맞는 적절하게 스레드에 작업을 분배한다.

 

따라서, DispatchQueue.main.asyncDispatchQueue는, GCD를 이용하기 위한 명령어인 것이다.

 

 

2. GCD's Queue

    1. Main Queue: main thread인 serial queue를 사용.

                                  ( serial queue: 한 작업을 하나씩 실행하는 것. 직렬 형태. )

    2. Global Queue: 전체 시스템에 공유되는 concurrent queue를 사용.

                                  ( concurrent queue: 동시에 작업을 실행할 수 있다. 병렬 형태.)

    3. Custom Queue: serial또는 concurrent queue를 생성

 

우린 아까 보았던 DispatchQueue.main.async에서 .main까지를 조사하였다.

 

 

3. Synchronous / Asynchronous

클로저 내에서 실행할 단일 작업에 대한 특성을 정의해주는 것이다.

synchronous: 큐에서 실행중인 작업이 끝날 때까지 다음 작업을 진행하지 않음.

asynchronous: 큐에 작업이 있어도 다음 작업을 비동기로 동시에 진행함.

+) asyncAfter(deadline: Float): 일정 시간 후 비동기로 진행하는 메소드

DispatchQueue.main.asyncAfter(deadline: .now() + 1.3){ }

이렇게 .async까지 DispatchQueue.main.async에 대한 분석이 끝났다.

 

 

+) 오류: DispatchQueue.main.sync

DispatchQueue.main.sync{}

위처럼 main.sync를 사용할 시에는 오류가 날 것이다.

 

앞서 언급했듯, main queue는 serial(직렬) queue이다.

그래서 main queue에서 sync를 호출하게 되면 queue를 block하고 우리가 넣은 작업이 완료되기를 기다릴 것인데,

queue가 이미 block되어있기 때문에 deadlock에 빠지게 된다.

⌘ Deadlock(교착 상태): 두 개이상의 thread에서 서로 lock이 해제되기만을 기다리고 있는 상태를 말한다.

 

+) 심화 : QoS

 DispatchQueue.global(qos: .userInteractive){ }

QoS(Quality of Service): 작업의 중요도를 정의한 개념

multi-threading시 작업을 concurrent하게 처리하고자 할 때 작업에 중요도를 두어 사용 

잘 사용하면 좀 더 반응성이 좋고, 자원 관리가 효율적인 앱을 만들 수 있을 것이다. 

 

<가장 높은 우선순위>

1. userInteractive:  바로 작동(최고 우선순위를 가지므로, UI 상호작용 등에 쓰인다. 그러나 main thread와는 확연히 다르다!)

2. userInitiated : Async 처리.

3. default 

4. utility :  계산, I/O, N/W 등에 사용

5. background

<가장 낮은 우선순위 >

 

 

 

📌 마치며

해당 코드는 URLSession에 대해 공부하다 발견한 코드였다.

URLSession은 main이 아닌 별도 스레드에서 작동하도록 이미 설계 되어 있다.

따라서 reloadData()같은 UI관련 코드는 아무 처리를 해주지 않으면 Error가 생긴다.

mainQueue에서 처리되도록 DispatchQueue.main을 통해 main에서 async(비동기)로 작업되도록 설정이 필요한 것.

            

               

 

 

 

오류 및 지적사항은 댓글로 남겨주시면 감사하겠습니다!

참고 : 

https://velog.io/@raejoonee/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EC%B0%A8%EC%9D%B4 ,

https://youtu.be/x-Lp-h_pf9Q

https://ios-development.tistory.com/96

https://zeddios.tistory.com/519

Comments