학습 목표
- Swift Concurrency 등장 이유
- Task 역할
Swift Concurrency 소개(= 등장 배경)
WWDC 2021년에 소개된 동시성 프로그래밍 API
async와 await 키워드로 비동기 태스크 종료 후 코드를 작성할 수 있다.
await로 중지되면, 이후 사용해야 하는 데이터를 Heap 영역에 저장하고, 이후 다시 돌아오면 꺼내서 사용한다.
GCD와 비교하며 왜 등장하게 되었는지 살펴보겠다.
가독성 & 에러처리 관점
가독성
기존의 GCD는 비동기 작업이 끝났는 지의 여부를 Completion Closure를 통해 알려준다.
그러면 A 작업이 끝나면 B, B 작업이 끝나면 C, … 이를 비동기로 처리한다면 무수히 많은 Depth가 생겨 들여쓰기에 의해 가독성이 낮아질 것이다.
반면 Swift Concurrency는 아래와 같이 동작한다.
위 사진들의 코드는 동일한 로직인 것.
await 키워드를 통해 실제 비동기 코드이지만, 동기처럼 보이게 하는 효과를 지녀 가독성을 증가시킬 수 있다.
에러 핸들링 안정성
URLSession을 통해서 이미지를 다운 받는 메소드가 있다고 하자
이미지 내려받는 걸 실패했을 때 예외처리하는 상황으로 둘을 비교해보겠다.
GCD는 이미지를 성공적으로 내려받으면 컴플리션 핸들러의 첫 번째 파라미터로 이미지를 넘겨준다.
그러나 상태코드가 200이 아니거나, 내려받은 data가 Nil인 경우 nil을 줘야 한다.
개발자가 실패했을 때에 대한 에러처리를 잘 하면 문제가 없지만, 휴먼 에러등의 이유로 컴플리션 클로저 호출을 빼먹으면 문제가 될 수 있다.
매번 확인해야 하는 번거로움이 있음
추가로, Result를 쓰면 가독성은 더 심각해짐
그래서 Swift Concurrency에서는 컴플리션 핸들러를 사용하지 않는다.
대신 do-catch 혹은 gaurd에 의해 Error를 던져주는 식으로 처리를 할 수 있는 것.
이러면 실패했을 때 컴플리션 핸들러를 빼먹어도 문제가 되지 않는다.
- 콜백을 안 해도 되니까 가독성도 좋아짐
성능적 관점
스레드 생성량과 Context Switching 수를 비교해 보는 과정
스레드 관점
GCD
GCD는 Thread Explosion을 조심해야 한다. (폭발이 아니라 너무 많이 생성되는 것 ㅇㅇ)
스레드를 너무 많이 만들면 컨텍스트 스위칭이 많아지고, 성능이 오히려 저하된다.
너무 많은 스레드 블록에서의 메모리 오버헤드, 스케줄링 오버헤드 등이 문제라 Thread Explosion을 예방하는 안전한 코드 작성이 필요하게 된다.
Swift Concurrency
반면, Swift Concurrency에서의 동작
비동기 컨텍스트에서 await로 비동기(= async로 선언된) 메소드를 호출할 경우, 동일한 스코프의 await 아래에 있는 코드들은 비동기 메소드가 끝날 때까지 대기한다.
CPU 제어권을 갖고 있는 스레드가 위 지점을 만나면, 시스템에게 async 메소드의 작업을 해야해!라고 알려준다. 또한, 해당 스레드는 CPU 제어권을 포기하여 다른 작업을 해당 스레드에서 할 수 있게 준비한다.
위처럼 하는 이유는 다음과 같다.
스레드가 무의미하게 CPU 제어권을 잡아 낭비하는 현상을 막을 수 있고, 시스템은 제어권이 없는 스레드에게 우선순위가 높은 작업들을 할당해줄 수 있게 된다.
GCD와 달리 스레드를 무작정 만들어서 비동기 작업을 진행하는 게 아니라, 위처럼 스레드를 재사용하며 처리한다.
그리고 기존의 GCD에서 컨텍스트 스위칭으로 스레드 별로 작업을 처리했다면, Swift Concurrency에서는 위 방식으로 실행할 작업을 함수 호출 수준의 비용만으로 해결했다.
등장 배경에 대한 개요라 두괄식으로 장점만 적었는데, 이에 대한 원리와 더 자세한 설명은 아래 async 에서의 스레드 제어권과 스택 프레임에서 설명하겠다.
후술하겠지만 Swift Concurrency에서는 Actor가 Thread를 재활용하고,
Thread의 개수를 Core의 개수와 동일하게 제한해서 이 문제를 해결한다.
우선순위 역전
GCD로 동시성 프로그래밍을 할 경우, 우선순위 역전이 발생할 수 있다고 한다.
하나의 큐에서 QoS가 각기 다른 작업이 담길 수 있는 것.
Background QoS인 작업이 큐에 추가되고, User Initiated 작업이 추가됐다고 가정하겠다.
그러면 background 작업들의 우선순위를 User Initiated로 올려서 새로 추가된 태스크가 너무 기다리지 않게 함
이게 FIFO 방식이라 그런듯
반면 Swift Concurrency는 FIFO가 아니므로 우선순위가 높은 애들을 먼저 처리해줄 수 있음
Task에 priority를 부여해서 앞에 작업이 쌓여있더라도 높은 우선순위 작업이 들어오면 해당 작업 먼저 수행시킬 수 있다.
정리
GCD의 개선사항들
- 가독성 (콜백 & 들여쓰기)
- 에러처리
- 코어 당 스레드 배정, 스레드 관점에서의 성능
- 우선순위 높은 작업부터 처리 가능
https://engineering.linecorp.com/ko/blog/about-swift-concurrency
Swift Concurrency에 대해서
안녕하세요. iOS Platform Dev 팀의 김윤재입니다. 1편에서 설명드렸던 것과 같이, 온보딩 스터디에서 공부한 내용 중 저에게는 동시성 프로그래밍(concurrency)에 관한 내용이 가장 도움이 되었습니다.
engineering.linecorp.com
'📱 iOS > Swift Concurrency' 카테고리의 다른 글
[Swift Concurrency 8편] Task와 구조화된 동시성(= Structed Concurrency) (0) | 2025.02.26 |
---|---|
[Swift Concurrency 7편] 비동기 호출에서의 스레드 제어권 (0) | 2025.02.26 |
[Swift Concurrency 5편] 동시성 프로그래밍 with GCD (0) | 2025.02.25 |
[Swift Concurrency 4편] 직렬-동시, 동기-비동기 (0) | 2025.02.24 |
[Swift Concurrency 3편] 공유자원과 임계영역 (0) | 2025.02.23 |