Swift Concurrency 7편 (Unstructured + Detached Task)

김재형·2024년 6월 26일
0
post-custom-banner

들어가기에 앞서

해당글은 Swift Concurrency 1편부터 이어지는 내용입니다.
꼭 처음부터 보고 봐주시길 바랍니다.

Unstructured tasks

저번시간에는 구조화가 되어 있음으로 즉,
부모 - 자식 관계나 범위가 계층 구조로 이루어져 있었습니다.
하지만 특정한 Task 에는 구조화가 없을 경우도 있죠.

import Foundation

// 비동기 작업을 수행하는 함수
func fetchData(url: URL) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

func performUnstructuredTask() {
    // 명시적으로 Task를 생성합니다.
    let task = Task {
        do {
            let url = URL(string: "https://KingJaeHyung.com/v1")!
            let data = try await fetchData(url: url)
            print("Data: \(data)")
        } catch {
            print("Error: \(error)")
        }
    }
    
    // 5초뒤, 작업 캔슬
    Task {
        try await Task.sleep(for: .secondes(5))
        task.cancel()
    }
}

// 메인 실행
performUnstructuredTask()

하지만 해당 방법은 부모 작업이 끝나더라도 계속 실행될 수 있습니다. 좀 더 명확하게 접근해 보죠

import Foundation

// 비동기 작업을 수행하는 함수
func fetchData(url: URL) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

// 부모 작업
func parentTask() async {
    print("Parent task started")
    
    // Unstructured task 생성
    let unstructuredTask = Task {
        do {
            let url = URL(string: "https://KingJaeHyung.com/v1")!
            let data = try await fetchData(url: url)
            print("Unstructured task data: \(data)")
        } catch {
            print("Unstructured task error: \(error)")
        }
    }
    
    // Parent task를 잡시 대기 시킵니다.
    try await Task.sleep(for: .secondes(15))
    print("부모님이 일을 마치셨다!")
    // 부모 task가 끝나더라도 unstructuredTask는 계속 실행됩니다.
    
    // 해제하려면 강제로 unstructuredTask를 취소해야 합니다.
    // unstructuredTask.cancel()
}

Task {
    await parentTask()
}

즉 비구조화 작업은 위 코드와 같이 독립적 으로 생성됩니다.
즉(2) 부모 작업의 수명을 벗어나 독립적으로 관리해야 할 때 좋죠.
다만, 이는 즉 방대해질수록 관리하기가 어려워집니다.
명시적으로 작업을 취소하여야 하기 때문이죠.

Detached tasks

Unstructured Concurrency와 마찬가지로 Detached tasks는 가장 높은 유연성을 제공합니다.
즉 언제든 어디든 비동기 코드를 실행 할수 있으며, 부모 에게 어떠한 것도 상속 받지 않습니다.
Actor, prioirty 마저도 상속 받지 않죠.
즉 다시말해, 컨텍스트 상속을 받지 않습니다.

import Foundation

// 비동기 작업을 수행하는 함수
func fetchData(url: URL) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

// 부모 작업
func parentTask() async {
    print("Parent task started")
    
    // detachedTask task 생성
    let detachedTask = Task.detached { // 여기를 주목!
        do {
            let url = URL(string: "https://KingJaeHyung.com/v1")!
            let data = try await fetchData(url: url)
            print("detachedTask task data: \(data)")
        } catch {
            print("detachedTask task error: \(error)")
        }
    }
    
    // Parent task를 잡시 대기 시킵니다.
    try await Task.sleep(for: .secondes(15))
    print("부모님이 일을 마치셨다!")
    // 부모 task가 끝나더라도 detachedTask 계속 실행됩니다.
    
    // 해제하려면 강제로 detachedTask 취소해야 합니다.
    // detachedTask.cancel()
}

Task {
    await parentTask()
}

여기서 중요한 점은 컨텍스트 상속 에 차이가 있습니다.
Unstructured Task 는 부모로 부터 상속을 받습니다. 즉 Actorprioirty 를 상속은 받는다는 말이죠
하지만 Detached tasks는 부모로 부터 아무것도 받는 것이 없습니다.

핵심 POINT

생성 방법

  • Detached Tasks: Task.detached 를 사용하여 생성합니다.
  • Unstructured Tasks: Task { --- } 를 사용하여 생성합니다.

취소 상속

  • 두개 모두 부모의 취소 상태상속받지 않습니다.

컨텍스트 상속

  • Detached Tasks: 부모의 컨텍스트를 상속받지 않습니다. 즉, 독립적 으로 실행됩니다. ( 글로벌 )
  • Unstructured Tasks: 부모의 컨텍스트를 상속받습니다. 예를 들어서, 부모 작업이 특정 액터나 작업 그룹 내에서 동작 중이였다고 가정하면, Unstructured task도 해당 컨텍스트 내에서 실행됩니다.

마치며...

헷갈리는 부분이 많아서. 차이점을 명확하게 짚고 가시면 좋을것 같습니다.
다음 시간에는 SwiftConcurrency Data Race 1부 로 돌아 오겠습니다.
감사합니다.

profile
IOS 개발자 새싹이
post-custom-banner

0개의 댓글