개요
동시성 프로그래밍 이론
동시성 프로그래밍: 여러 작업들을 동시에 처리하는 것처럼 보이게 하는 것
이번 주는 동시성 프로그래밍과 이를 위한 기법 GCD를 배워보겠음

(사진 날진님 블로그 제공)
좌측에 있는 노동자 = Thread
우측에 있는 일 = Task

URLSession.dataTask() 처럼 기본적으로 백그라운드 스레드에서 비동기로 실행되는 게 아니라면
우리는 여태껏 메인 스레드에 Task를 할당 해주고 있었다.
예를 들어 50MB 이미지를 Data 클래스로 변환하는 작업을 하더라도 그냥 메소드에서 변경하고 있었다면 이는 메인 스레드에서 작업하는 것.
메인 스레드 역할
메인 스레드는 UI를 그리는 일을 해야 한다.
- 뷰 그리는 UIKit의 속성을 백그라운드에서 돌리기 위해 Thread-safe하게 하려면 성능저하가 발생하기 때문
- 메인 런루프가 뷰 업데이트를 관리하는 View Drawing Cycle을 통해 뷰를 새로 그리는데, 백그라운드에서 각자 런루프에 따라 뷰를 그리면 제멋대로 동작할 수 있음
그래서 우리는 작업을 잘 나누어서 다른 스레드에서도 동시에 작업하도록 하는 것이 핵심이고, 이것이 곧 동시성 프로그래밍
이다.

이를 위해 DispatchQueue를 이용하고, 이 큐에 넣어주면 알아서 OS가 다른 스레드로 작업을 분배해준다.
동시성 프로그래밍을 위한 iOS 기술들
1. Operation
Operartion Queue
GCD 위에서 동작함 (업그레이드 버전이나, 구현이 복잡)
GCD와 달리 아래의 작업이 더 가능함
TMI) URLSession은 Operation Queue라고 함
- 동시 실행할 수 있는 최대 수 지정
- 동작 일시중지 및 취소
2. GCD
Grand Central Dispatch
GCD에서 사용하는 큐 이름이 DispatchQueue임

- global: 큐의 종류 (추가로 main이 있음)
- async: 비동기 (추가로 sync가 있음)
- 비동기의 경우 끝나는 시점은
completionHandler
를 통해 알게 됨
- 비동기의 경우 끝나는 시점은
- QoS에 따라 DispatchQueue 객체를 생성함
{ }에 Task 작업 단위를 넘겨주면 됨
async vs sync
작업을 보내는 시점에서 기다릴지 말지에 대해 다루는 것
concurrent vs serial
Queue(대기열)로 보내진 작업들을 여러개의 스레드로 보낼 것인지 한개의 스레드로 보낼 것인지에 대해 다루는 것
QoS (Quality Of Service)
DispatchQueue.global(qos: .utility).async { }
와 같이 우선순위를 지정할 수 있다.
종류
- userInteractive
- 사용자와 직접 상호작용을 기대함
- 메인 스레드에서 처리할 때 오래 걸리는 것들을 여기에 배치함
- default
- utility
- 프로그레스 바와 함께 길게 실행되는 것
- 데이터 다운로드 등
- background
- 덜 중요한 작업
응용
DispatchQueue.global(qos: .background).async(.utility)
Queue와 Task 각각 QoS가 다름, 둘 중 높은 걸 따라감
- Task QoS > Queue QoS
- Task의 우선순위가 큐보다 더 높은 경우 utility로 우선순위가 상승하게 됨
- Task QoS < Queue QoS
- Queue QoS 따라감

utility 작업이 먼저 큐에 들어가고, 이후 default 작업들이 들어감
그러나, utility가 끝날 때까지 queue의 QoS는 utility임
GCD 사용 시 주의할 점 (레퍼런스 링크)
메인 큐
에서 다른 큐로 작업 보낼 때sync 사용 X
- 현재 큐와 같은 큐에
sync X
Task B가 스레드에 할당되어 작업이 끝날 때 까지 기다림그러나, Task A를 수행하던 Thread2는 B가 끝날때까지 기다리느라 멈춰있음 - QoS를 다르게 해서 다른 큐를 쓰게 하면 데드락 발생 방지 가능
- GCD는 B를 어디에 할당할 지 고민하고 Thread 2에 할당해버리면
데드락이 걸림
메인 스레드에서
DispatchQueue.main.sync
사용 X- 똑같이 데드락 발생
- 값 캡처 주의 (링크)라이프사이클에 따라 다르나, 불안하면 걍 쓰셈
- 글로벌 변수를 내부에서 사용하지 않으면 retain cycle이 발생하지 않음
비동기 작업에서 컴플리션 핸들러의 존재 이유
async로 작업을 수행하면 끝나기를 기다리지 않고 다음 작업을 한다.
그럼 보낸 작업의 끝나는 유무를 어떻게 아냐? → 컴플리션 핸들러
내가 헷갈렸던 것

- 만약 프로세서가 1개라면 위 작업이 끝나면
6+a
초 일까,3+a
초 일까?결국 CPU는 한 번에 하나의 작업밖에 처리하지 못 하기 때문에 멀티 스레드로 돌려봤자 컨텍스트 스위칭하느라 6초에 a시간만큼 더 소요되는 것 - 멀티코어 프로세서에서 멀티 스레드를 해야 효과가 있음
- 6 + a 초임
- 그럼 단일 코어 프로세서에서 멀티 스레드를 하면 이점이 없나 ?단일 코어여도 멀티스레드는 장점이 있음I/O 요청하고 자기 할 일 하러 가면 되니까
- 작업을 비동기적으로 처리할 수 있으므로 I/O 작업에서 이점을 얻을 수 있음
- 답은 No
'📱 iOS > Swift Concurrency' 카테고리의 다른 글
[Swift Concurrency 7편] 비동기 호출에서의 스레드 제어권 (0) | 2025.02.26 |
---|---|
[Swift Concurrency 6편] Swift Concurrency 등장 배경 (1) | 2025.02.26 |
[Swift Concurrency 4편] 직렬-동시, 동기-비동기 (0) | 2025.02.24 |
[Swift Concurrency 3편] 공유자원과 임계영역 (0) | 2025.02.23 |
[Swift Concurrency 2편] Process & Thread (0) | 2025.02.22 |