Study

📚 Concurrency Docs 내 일부

_yunie 2024. 9. 11. 09:16

Tasks and Task Groups

  • Task
    • 비동기 작업 단위
    • Task는 한 번에 한 가지만 수행하지만 여러 Task를 작업할 경우, Task Group을 통해 동시에 실행되도록 해줄 수 있음
  • async-let
    • 실행될 작업에 대해 하위 작업이 생성됨
  • 계층 구조의 작업이 가지는 이점
    • 상위 작업에서 하위 작업이 완료될 때까지 대기
    • 하위 작업에서 작업의 우선순위를 높인다면 상위 작업의 우선순위도 함께 상향됨
    • 상위 작업이 취소되면 하위 작업이 자동으로 취소됨
    • 작업 내 데이터들이 하위 작업에서도 효율적으로 쓰일 수 있음
  • 작업 결과를 반환하지 않는 TaskGroup
// 작업의 그룹 생성 
await withTaskGroup(of: Data.self) { group in
    let photoNames = await listPhotos(inGallery: "Summer Vacation")
    for name in photoNames {
        group.addTask {
		        // 이미지 다운로드 하위 작업 
            return await downloadPhoto(named: name)
        }
    }

		// 이미지 다운로드가 끝나면 show 
    for await photo in group {
        show(photo)
    }
}
  • 작업 결과를 반환하는 TaskGroup
// 모든 하위 작업이 끝날 때까지 대기 후 반환 
let photos = await withTaskGroup(of: Data.self) { group in
    let photoNames = await listPhotos(inGallery: "Summer Vacation")
  
    for name in photoNames {
        group.addTask {
            return await downloadPhoto(named: name)
        }
    }

    var results: [Data] = []
    for await photo in group {
        results.append(photo)
    }

    return results
}

 

Task Cancellation

  • 작업의 취소 상태를 확인해 적절한 대응 가능
    • CancellationError 같은 에러 발생
    • nil 또는 빈값 반환
    • 부분 완료 작업 반환
  • 작업 취소 방식
    • Task.checkCancelation()
      • 작업이 취소됨녀 에러를 던짐
    • Task.isCancelled
      • 작업을 안전히 정지하거나 네트워크 통신을 중지, 임시 데이터를 지울 때
      • 해당 작업 외부에서 작업이 취소되었는지 확인하는 타입 프로퍼티
let photos = await withTaskGroup(of: Optional<Data>.self) { group in
    let photoNames = await listPhotos(inGallery: "Summer Vacation")
    for name in photoNames {
        // 그룹이 취소되지않았을 때, 하위 작업 생성 
        let added = group.addTaskUnlessCancelled {
		        // 작업이 취소됐다면 nil 반환 
            guard !Task.isCancelled else { return nil }
            // 취소되지않았다면 이미지 다운로드 
            return await downloadPhoto(named: name)
        }
        guard added else { break }
    }

		// 그룹은 nil값을 스킾하고 반환 
    var results: [Data] = []
    for await photo in group {
        if let photo { results.append(photo) }
    }
    return results
}
  • 만약 취소된 상황을 즉시 알고싶다면 Task.withTaskCancellationHandler 활용
    • 취소 여부를 확인후 작업을 조기 종료시킴
    • 해당 핸들러가 실행되면 작업과 해당 핸들러 간 상태를 공유하지 않아야 함
      • Data Race 발생 가능성 존재
let task = await Task.withTaskCancellationHandler {
    // ...
} onCancel: {
    print("Canceled!")
}

// ... some time later...
task.cancel()  // Prints "Canceled!"

 

Unstructured Concurrency

  • 상위 작업이 없는 상태에서 비동기 작업이 일어나는 경우
  • 작업 생성 시, Task.init(priority:operation:)
  • 분리된 작업 생성 시, Task.detached(priority:operation:) 사용

 

👀 etc

@Sendable

  • Data Race 없이 Concurrency 작업 간 공유할 수 있음을 의미하는 데이터 타입
  • 채택 가능 타입
    • Value types
    • Reference types with no mutable storage
    • Reference types that internally manage access to their state
    • Functions and closures (by marking them with @Sendable)
  • 컴파일러 적용없이 Sendable 채택 시, @unchecked Sendable 활용
    • 같은 파일 내에 있어야한다는 규칙을 비활성화 시킴
    • Lock 또는 Queue로 해당 상태에 대한 모든 접근을 보호해 확인되지않은 sendable의 책임을 짐
    ⇒ 문제가 생기더라도 개발자가 책임을 진다
  • wwdc 내 왈 : 클로저 내에서 mutable 변수를 캡쳐하는 것이 제한됨
    • 작업이 시작된 후 수정될 수 있기때문
      • = 공유자원은 여러 스레드에서 접근해도 안전해야함
      • ex) Actor, Class 등

'Study' 카테고리의 다른 글

📚 Closure  (0) 2025.06.02
🍎 WWDC21 : Explore structured concurrency in Swift  (0) 2024.09.07
👀 Continuation 알아보기  (0) 2024.08.24
🍎 WWDC21: Meet async/await in Swift  (0) 2024.08.20
📚 ARC Docs 정리  (0) 2024.08.06