SC #10: 분리된 작업
Source: Dev.to
분리된 작업(Detached Task)은 구조화된 동시성 컨텍스트 밖에서 비동기적으로 작업을 실행합니다. 이 컨텍스트를 상속받지 않으면 다음을 상속받지 않게 됩니다:
- 컨텍스트의 우선순위
- 컨텍스트의 취소 상태.
작업을 생성하려면 정적 메서드 Task.detached(name:priority:operation:)를 사용합니다:
Task.detached {
// Ejecuta una operación de forma asíncrona fuera del contexto estructurado de concurrencia
}
기본 예시
func detachedTaskExample() async {
func asyncFunc(_ string: String) async {
print(string)
}
await asyncFunc("\(1)")
Task.detached {
await asyncFunc("\(2)")
}
await asyncFunc("\(3)")
}
위 코드는 다음과 같은 결과를 출력할 수 있지만, 순서는 보장되지 않습니다:
1
3
2
분리된 작업의 취소 위험
다음 예시를 고려해 보세요. 여기서 longRunningAsyncOperation은 취소되기 전에 끝나지 않는 비동기 작업이며, detachedTaskExample은 이를 서브태스크로 호출한 뒤 분리된 형태로 호출하기 위해 Task.detached를 생성합니다:
func detachedTaskExample() async {
let outerTask = Task {
// Esta subtarea sí se va a cancelar
await longRunningAsyncOperation(1)
// Esta tarea desacoplada NO hereda el estado de cancelación
Task.detached(priority: .background) {
// Por eso este checkCancellation no hace nada
try Task.checkCancellation()
// Por eso esta subtarea no se va a cancelar
await longRunningAsyncOperation(2)
}
}
outerTask.cancel()
}
private func longRunningAsyncOperation(_ id: Int) async {
do {
print("Empezando tarea \(id)")
try await Task.sleep(for: .seconds(5))
print("Terminé la tarea")
} catch {
print("\(#function) - Tarea \(id) falló con error: \(error)")
}
}
이 예시의 출력은 다음과 같습니다:
Empezando tarea 1
longRunningAsyncOperation(_:) - Tarea 1 falló con error: CancellationError()
Empezando tarea 2
Terminé la tarea
이 예시에서:
- 첫 번째
longRunningAsyncOperation호출은 취소된Task의 구조화된 컨텍스트 안에서 실행되므로 정상적으로 취소됩니다. - 두 번째 호출에서는
try Task.checkCancellation()을 실행해도 취소를 감지할 방법이 없으며, 취소 상태를 상속받지 않기 때문입니다.
분리된 Task를 취소하려면 해당 작업에 대한 참조를 유지하고 수동으로 취소해야 합니다. 이러한 작업은 참조가 해제될 때 자동으로 취소되지 않으므로 명시적으로 관리해야 합니다.
언제 분리된 작업을 사용해야 할까?
다음과 같은 상황에서 분리된 작업을 만드는 것이 적절합니다:
- 작업을 독립적으로 실행할 수 있을 때.
- 컨테이너 컨텍스트와 연결될 필요가 없을 때.
- 컨테이너가 취소되더라도 작업이 계속 실행되어도 괜찮을 때.
유효한 예시 (self에 대한 참조가 없음):
Task.detached(priority: .background) {
await DirectoryCleaner.cleanup()
}