본문 바로가기

프로그래밍/iOS,macOS

[concurrency] Task, TaskGroup, task timeout

Task 

@frozen struct Task<Success, Failure> where Success: Sendable, Failure: Error
init(priority: TaskPriority?, operation: () async -> Success)
init(priority: TaskPriority?, operation: () async throws -> Success)

 

TaskGroup

@frozen struct TaskGroup<ChildTaskResult> where ChildTaskResult: Sendable

TaskGroup은 별도의 메서드를 사용해 생성해야 하며, 생성된 task 내에서만 사용해야 함.

 

주요함수

func addTask(priority: TaskPriority?, operation: () async -> ChildTaskResult)
func addTaskUnlessCancelled(priority: TaskPriority?, operation: () async -> ChildTaskResult) -> Bool
func next() async -> ChildTakResult?
func waitForAll() async
func cancelAll()

 

addTask : 그룹에 task 추가
addTaskUnlessCancelled : 그룹이 종료(cancel)되지 않았으면 task 추가
next : 다음 Task가 완료되길 기다려 해당 값을 리턴
waitForAll : 모든 Task들이 완료될때까지 대기
cancelAll: 모든 그룹내 Task 취소

 



 

TaskGroup 생성

새로운 태스크 그룹 생성을 위한 Concurrency 메서드

func withTaskGroup<ChildTaskResult, GroupResult>(
    of childTaskResultType: ChildTaskResult.Type, 
    returning returnType: GroupResult.Type = GroupResult.self, 
    body: (inout TaskGroup<ChildTaskResult>) async -> GroupResult) async -> GroupResult where ChildTaskResult: Sendable
    
    
func withThrowingTaskGroup<ChildTaskResult, GroupResult>(
    of childTaskResultType: ChildTaskResult.Type, 
    returning returnType: GroupResult.Type = GroupResult.self, 
    body: (inout ThrowingTaskGroup<ChildTaskResult, Error>) async throws -> GroupResult) async rethrows -> GroupResult where ChildTaskResult: Sendable

 

 

TaskGroup을 사용한 타임아웃 처리

원하는 task를 별도 task와 타이머 task로 task group 에 추가하고, next() 를 통해 완료된 task의 결과를 체크하는 형태로 타임아웃을 구현할 수 있다.

enum MyError: LocalizedError {
    case timeout
    
    var localizedDescription: String {
        switch self {
        case .timeout:
           return "timeout"
        }
    }
    
    var errorDescription: String? {
        localizedDescription
    }
}

Task {
    try await withThrowingTaskGroup(of: Sendable.self) { group -> Sendable in
        group.addTask {
            .
            .
            .
            return sendable
        }

        group.addTask {
            try await Task.sleep(nanoseconds: UInt64(1 * 1_000_000_000))
            throw MyError.timeout
        }

        guard let result = try await group.next() else {
            throw CancellationError()
        }

        group.cancelAll()
        return result
    }
}