일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- completion handler
- 파노라마 뷰
- 360도 이미지
- native
- launchscreen
- React-Native
- 리액트 네이티브
- ssot
- 360도 뷰어
- SwiftUI
- React Native
- panorama view
- 뷰 생명주기
- launch screen
- 스켈레톤 통합
- react-native-fast-image
- 네이티브
- 라이브러리 없이
- 리액트
- 앱 성능 개선
- requirenativecomponent
- 뷰 정체성
- react
- ios
- @sendable
- 360도 이미지 뷰어
- data driven construct
- 명시적 정체성
- Android
- 구조적 정체성
- Today
- Total
Neoself의 기술 블로그
Explore structured concurrency in Swift 정리 본문
위 예제는 비동기 및 동시성 코드를 사용하지만, 이러한 코드 작성을 용이하게 하기 위해 구조적 프로그래밍을 활용하지 못하고 있다
즉, 함수가 호출되었을 때 값을 반환하지 않으며, 결과나 오류를 완료 핸들러에 전달하기 때문에, 오류 처리를 위한 구조적 제어 흐름을 사용할 수 없으며, 썸네일 처리를 위한 루프 또한 사용이 불가하다.
Task
- 프로그램의 실행 컨택스트
- 비동기적으로 실행될 코드의 독립적인 단위
- Swift 컴파일러는 이러한 태스크들을 스레드에 할당하여 병렬성을 확보
- 각 작업는 다른 실행 컨텍스트(Executing Context)와 동시에 실행되며, 안전하고 효율적일 때 자동으로 병렬로 실행되도록 스케줄링됩니다.
* async 함수를 호출한다고 해서 호출을 위한 새로운 작업이 생성되지 않으며, 이는 직접 명시적으로 생성되어야 함.
태스크: 실행될 코드의 논리적인 단위
스레드: 실제로 코드를 실행하는 물리적인 단위
일반적으로 하나의 태스크는 하나의 스레드에서 실행되지만, 스레드는 여러 태스크를 번갈아 가며 실행할 수 있습니다. 즉, 스레드는 여러 실행 컨텍스트를 시분할 방식으로 처리할 수 있습니다.
태스크는 스레드 풀에서 스레드를 할당받아 실행됨
스레드 풀은 프로그램 실행 전에 미리 생성해 둔 스레드들의 집합
Task 종류
1. async-let 바인딩:
구조적 프로그래밍의 본질을 포착하면서, 동시성을 추가하기 위한 가벼운 구문 제공
일반적인 let 바인딩: 실행 흐름이 하나
- 등호 오른쪽의 초기화 표현식과 왼쪽의 변수 이름으로 나뉨
- let 바인딩에 도달하면 위에서의 데이터 다운로드 작업과 같이 초기화 프로그램이 평가되어 값을 생성하고, 후속 구문 진행 이전에 값을 변수에 바인딩함.
async let 바인딩: 데이터 다운로드 완료 이전까지 다른 작업을 계속 수행할 수 있도록 Concurrent Binding(동시 바인딩)으로 바꿔줌.
1. 동시 바인딩 평가를 위해 Swift는 먼저 새로운 하위 태스크(자식 태스크)를 생성
2. 초록색 화살표는 자식 작업의 실행 컨텍스트로 즉시 데이터 다운로드 시작
3. 하얀색 화살표는 부모 작업을 위한 것으로, 즉시 변수를 placeholder 값에 바인딩
4. 자식 태스크로 데이터 다운로드되는 동안, 부모 작업은 동시 바인딩 다음에 오는 명령문 계속 실행
5. result 변수에 바인딩된 실제 값이 필요한 표현식에 도달하면, 부모는 자식 작업의 완료를 기다림.
동시성의 구조는 작업의 취소, 우선순위 및 작업 로컬 변수를 비롯한 여러 속성에 영향을 미침.
부모 작업은 모든 자식 작업이 완료되기 전까지 작업을 마칠 수 없음.
위는 메타 데이터 작업 수행 이후, 이미지 데이터 작업을 수행하는 코드
이 코드에서 만일 try await metadata에서 오류를 던지며 완료되면,
1. 대기되지 않은 작업을 취소된 것으로 Swift는 자동으로 표시(이 때 해당 작업의 모든 하위 작업도 같이 취소)
2. 함수 종료 이전에 해당 작업의 완료를 기다림
3. 즉 취소된 것으로 표시만 하지, 작업 자체가 중단되지는 않음.
4. 결국, fetchOneThumbnail은 모든 구조화된 작업이 완료되면 오류를 발생시켜 최종 종료됨.
이로 인해, 직접 마무리하는 것을 기여함으로써, 각 작업의 수명주기를 관리.
즉 동시에 실행되는 작업이 에러를 던져 작업이 중단되어야 하는 상황이여도, 오래 걸리는 계산을 포함하는 비동기 작업을 실행하고 있다면 해당 작업의 종료를 기다려야 하기에 불필요한 오버헤드가 발생.
오버헤드: 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리
때문에, 실행 중 취소 상태를 확인하고 적절하게 종료하는 등의 구현이 필요하기에 협력적 취소가 필요
2. Group Task
async-let 보다 더 많은 유연성 제공
현재 좌측 코드의 경우 다음 루프 반복이 시작되기 전에 두 자식 작업이 완료되어야 합니다.
하지만, 동시적으로 모든 썸네일 다운로드 작업을 실행하고자 할 경우에는, Group Task를 활용
우측의 withThrowingTaskGroup 함수를 호출할 경우,
- group의 async 메서드를 호출해, 그룹에 오류발생이 가능한 자식작업을 생성할 수 있음
- 추가된 자식작업들은 즉시 임의의 순서로 실행을 시작하며, 범위를 벗어나면 모든 작업완료가 암묵적으로 기다려짐
위 코드와 같이 여러 자식 작업이 단일 딕셔너리에 동시에 접근할 경우, 데이터 race 상황이 발생할 수 있음. 이는 컴파일 에러로 확인이 가능함.
Task 블록이 수행하는 일 중 하나는, 생성 시 Sendable 클로저를 사용하기에 수행되는 작업이 공유 가능한 안전한 값을 캡처하도록 제한하는 것
= 어휘적 컨텍스트 내에 변경 가능한 변수를 캡처할 수 없음
이는 위 코드처럼 각 자식 작업 내부에서, 딕셔너리에 직접 쓰는 대신, 부모가 처리할 수 있도록 키-값 튜플을 반환하도록 한 후, for-await 루프를 사용해 부모 작업에서 각 자식작업의 결과를 사용해 저장.
여기서 for-await 루프는 순차적으로 실행되기에 부모 작업은 각 키-값 쌍을 딕셔너리에 안전하게 추가 가능.
3. Unstructured Task
많은 유연성을 얻을 수 있으나, 수동 관리가 많이 필요
명확한 계층 구조에 속하지 않은 상황에 사용
- 비동기 코드가 아닌 곳에서 비동기 계산 수행할 경우, (상위 작업이 없을 수 있음)
- 작업에 원하는 수명이 단일 함수 제약을 벗어 나는 경우, (Delegate 메서드에 특히 자주 사용)
-델리게이트 메서드는 비동기가 아니기에 비동기 함수 호출을 await할 수 없으며, 썸네일 다운로드 작업의 수명을 델리게이트 메서드 범위에 제한하고 싶지 않음.
Task 클로저로 비동기 작업을 옮길 경우, 작성 샘성 시점 도달 시 Swift는 해당 작업을 원래 범위와 동일한 액터에서 실행되도록 예약하며, 제어는 호출자에게 반환됩니다. 이후, 썸네일 작업은 실행할 여유가 있을 시점에, 메인스레드에서 실행됩니다.
이러한 비구조적 작업은 수명이 시작된 범위에 묶여있거나, 원본이 비동기일 필요조차 없기에 어디에서나 범위가 지정되지 않은 작업을 생성할 수 있지만, 취소 및 오류가 자동으로 전파되지 않음
때문에, 위와 같이
- 작업을 딕셔너리 자료구조에 저장하여 나중에 취소처리할 수 있도록
- 작업이 완료 시, 딕셔너리에서 제거
- 화면에서 보이지 않게 될 경우, 이를 담당하는 델리게이트 메서드에서 cancel 메서드 호출하여 작업 취소
4. Detached Task
컨텍스트와 독립적, 비구조적, 원래 스코프에 묶여있지 않음, 원래 스코프에서 아무것도 가져오지 않음
이때, 작업 그룹(task group)을 사용하여 여러 배경 작업을 관리하면, 상위 작업을 취소함으로써 모든 자식 작업을 동시에 취소할 수 있으며, 이는 자동으로 전파된다.
또한, 자식 작업은 부모 작업의 우선 순위를 자동으로 승계하므로, UI 작업을 방해하지 않도록 배경 우선 순위를 쉽게 설정할 수 있다.
'개발지식 정리 > WWDC 정리' 카테고리의 다른 글
Protect mutable state with Swift actors 정리 (0) | 2025.04.03 |
---|---|
Meet async/await in Swift 정리 (0) | 2025.04.03 |
Understanding Swift Performance 정리 (0) | 2025.01.07 |
SwiftUI 심층정리 (0) | 2024.12.16 |
SwiftUI | PhaseAnimator, Keyframes으로 고급 애니메이션 구현하기 (6) | 2024.10.16 |