-
[Swift] TaskPriority, Task 우선순위 동작 이해하기Swift 2025. 3. 21. 10:40반응형
TaskPriority 는 말 그대로 Task의 우선순위를 뜻합니다.
직관적이고 간단한 개념이지만 Task의 우선순위와 관련한 개념에 대해 조금 더 확실하게 알아보겠습니다.# 기본 개념
TaskPriority는 스케줄링 힌트일 뿐, 반드시 이 우선순위에 따라서 실행된다는 보장은 없습니다.
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct TaskPriority : RawRepresentable, Sendable
케이스
case high // 사용자 인터페이스 등 즉시 반응이 필요한 작업 case userInitiated // 사용자가 명시적으로 시작한 작업, 빠르게 끝나야 함 case medium // 일반적인 기본 우선순위 (기본값) case low // 낮은 우선순위, 백그라운드 작업 case utility // 에너지 효율과 관련된 긴 작업 (ex. 다운로드) case background // 사용자 인식이 필요 없는 작업
# 우선 순위 동작
1. 실제로 어떤 Task가 먼저 실행될지는 Executor가 정한다.
The executor determines how priority information affects the way tasks are scheduled. The behavior varies depending on the executor currently being used. Typically, executors attempt to run tasks with a higher priority before tasks with a lower priority. However, the semantics of how priority is treated are left up to each platform and Executor implementation.
Task는 내부적으로 Executor 라는 실행 엔진에 의해 스케줄링 됩니다. 실제로 어떤 Task가 먼저 실행될지는 이 Executor가 결정합니다. 일반적으로는 우선순위가 높은 작업이 먼저 실행됩니다. 하지만 이건 Executor나 플랫폼에 따라 달라질 수 있습니다.
ex) iOS에서는 .userInitiated 작업이 .background 보다 먼저 실행되도록 노력하지만, 시스템 상황에 따라 달라질 수 있음
2. 자식 Task는 부모의 우선순위를 자동으로 상속한다.
Child tasks automatically inherit their parent task’s priority.
Task 안에서 또 다른 Task를 만들면 자식 Task는 부모의 우선순위를 물려받습니다.
Task(priority: .high) { await Task { // 이 자식 작업도 high 우선순위를 가짐 }.value }
3. Detached Task는 부모 Task의 우선순위를 상속받지 않음. 주의해서 사용할 것.
Detached tasks created by detach(priority:operation:) don’t inherit task priority because they aren’t attached to the current task.
Task.detached(priority:operation:)으로 생성한 Task는 부모 Task와 완전히 분리되므로, 부모 Task의 우선순위를 상속받지 않습니다. 따라서 우선순위를 지정하지 않는다면, 부모 Task의 우선순위가 아니라 기본값이 적용될 것입니다.
Don't use a detached task if it's possible to model the operation using structured concurrency features like child tasks.
Child tasks inherit the parent task's priority and task-local storage, and canceling a parent task automatically cancels all of its child tasks.
You need to handle these considerations manually with a detached task.그리고 구현부 정의를 따라가보면 가능한 사용하지 말라고 주의를 주고 있습니다.
detached Task를 사용할 경우 우선순위, 부모 취소시 자동 취소 등을 모두 직접 관리해야 합니다.4. 우선순위가 일시적으로 상승하는 경우 (Priority Elevation)
In some situations the priority of a task is elevated — that is, the task is treated as if it had a higher priority, without actually changing the priority of the task.
...
priority elevation helps you prevent a low-priority task from blocking the execution of a high priority task, which is also known as priority inversion.특정 조건에서는 Task의 우선순위를 변경하지는 않지만 일시적으로 더 높은 우선순위처럼 취급됩니다.
이는 우선순위가 높은 Task가 너무 늦게 실행되는 것을 방지해 주고, 이를 priority inversion 이라고도 한다고 하네요.구체적으로 아래 2가지 케이스가 있습니다.
4-1. Actor 내부에서 우선순위가 높은 Task가 들어올 때
If a task runs on behalf of an actor, and a new higher-priority task is enqueued to the actor, then the actor’s current task is temporarily elevated to the priority of the enqueued task.
Actor 내부에서 Task가 실행 중인데, 더 높은 우선순위의 Task가 큐에 들어오면 현재 실행중인 Task의 우선순위가 일시적으로 상승합니다.
4-2. 높은 우선순위 Task가 get()을 호출하면, 기다리는 Task의 우선순위가 일시적으로 상승
If a higher-priority task calls the get() method, then the priority of this task increases until the task completes.
만약 high 우선순위 Task 에서 low 우선순위 Task 의 동작을 기다리고 있다면, 이 low Task를 빨리 끝내줘야 전체 흐름이 원활해지겠죠.
아래와 같은 상황이 될 것 같습니다.let handle = Task(priority: .low) { return await fetchSomething() } Task(priority: .userInitiated) { let result = try await handle.value // get()과 동일한 역할 }
# Detached Task를 써야만 하는 상황
보다보니 detached Task를 써야만 하는 상황이 궁금해졌습니다.
1. 부모 Task의 취소 영향을 받지 않고 독립적으로 실행해야 하는 경우
일반적으로는 부모 Task가 취소되면 자식 Task도 알아서 취소해주면 좋지만 그렇지 않은 경우가 있을 수 있습니다.
예를 들어, 앱이 백그라운드로 가더라도 데이터를 계속 저장하고 싶을 땐 부모 Task 와 무관하게 처리되는 게 좋을 수 있겠죠.2. 완전히 독립적인 환경에서 실행해야 할 때
로그 전송처럼 부모 Task 와 독립적으로 실행되길 원하는 동작이 있을 수 있습니다.
3. Actor 외부에서 실행해야 하는 코드가 있을 때
Actor 내부에서 실행중인 Task는 직렬로 실행됩니다. 하지만 일부 작업은 외부에서 병렬로 처리되어야 할 수 있습니다.
actor ImageProcessor { func processImage() async { // Actor 내부 작업 let image = await fetchImage() // Actor 외부에서 실행해야 할 작업 (병렬 실행) Task.detached { await filterImage(image) // Actor에 묶이지 않음 (다른 Executor에서 실행) } } }
4. 비동기 API가 없는 동기 코드를 실행해야 할 때
만약 오래 걸리는 동기 코드를 실행해야 한다면, 기존 task의 흐름을 방해하지 않고 독립적으로 실행해야 할 수 있습니다.
5. SwiftUI에서 UI와 독립적으로 백그라운드 작업이 필요할 때
그냥 Task { } 를 사용하면 SwiftUI 뷰가 다시 그려질 때 Task 가 취소됩니다. 이와 무관하게 Task 작업을 진행해야 한다면 필요할 수 있습니다. 하지만 역시 불필요하게 중복 진행될 수 있으니 더욱 주의해야 할 것 같습니다.
# Ref
https://developer.apple.com/documentation/swift/taskpriority
TaskPriority | Apple Developer Documentation
The priority of a task.
developer.apple.com
간단해 보이는 개념 같은데 하나하나 보다보니 글이 길어졌네요. 뭐 하나도 제대로 알기가 참 어려운 것 같습니다.
읽어주셔서 고맙습니다.반응형'Swift' 카테고리의 다른 글
Swift Seqeunce 이해하기 (0) 2025.03.27 [Swift] Sendable 이해하기 (0) 2025.02.11 [Swift] Actor 이해하기 (0) 2025.02.11 [RxSwift] RxRelay 그리고 Combine (0) 2024.06.25 Identifiable 프로토콜 기본개념 (0) 2023.07.31