Task란 무엇인가) Task의 정의

SteadySlower·2023년 8월 29일
0

iOS Development

목록 보기
31/38

안녕하세요.

이번에 실무에서 Combine으로 되어 있는 코드를 async/await로 수정하는 업무를 맡았습니다. 그 업무를 하면서 공부를 한 내용을 몇 개의 포스팅에 나누어서 소개하고자 합니다.

이번 포스팅에서는 Swift의 Task에 대해서 알아봅니다.

Task의 정의 및 동작

공식문서에 따르면 Task의 정의는 “A unit of asynchronous work.”로 해석하면 비동기 작업의 단위라고 할 수 있겠습니다. 코드를 예시로 설명해보겠습니다.

func taskExample() {
    print("동기 1")
    Task {
        print("비동기")
    }
    print("동기 2")
}

위 함수에서 print("동기 1")와 print("동기 2")는 동기적으로 실행됩니다. 반면에 Task는 비동기적으로 실행이 됩니다. Task를 만들면 {} 안에 정의된 작업을 비동기적으로 실행합니다. 즉 다른 쓰레드로 보내서 처리하고 해당 결과를 기다리지 않고 바로 코드의 다음 줄을 실행하는 것이죠.

쓰레드를 출력을 해보면 결과는 아래와 같습니다. Task 안의 작업은 메인쓰레드가 아닌 다른 쓰레드에서 실행되는 것을 볼 수 있습니다.

func taskExample() {
    print(Thread.current)
    Task {
        print(Thread.current)
    }
}
<_NSMainThread: 0x600001704280>{number = 1, name = main}
<NSThread: 0x6000017101c0>{number = 2, name = (null)}

아래 처럼 Task를 여러 개 만든다면 어떻게 될까요? 이런 경우 시스템에서 알아서 여러 개의 쓰레드에 분배해서 코드를 실행합니다. (중복될 수도 있습니다.) 즉, Task 각각은 서로 독립된 작업입니다. 같은 함수 안에서 만들어진 Task라도 말입니다.

func taskExample() {
    print(Thread.current)
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
    Task { print(Thread.current) }
}

Task의 기능들 (기본편)

이번 포스팅에서 Task의 참조, 취소, 리턴값에 대한 아주 기본적인 정보를 다루고 다음 포스팅에서 실제로 예시 코드를 통해서 Task의 작동원리를 보도록 하겠습니다.

참조

Task 객체는 만들자마자 코드 블럭에 정의된 코드를 실행합니다. 따라서 Task를 실행하기 위해서 별도의 작업이 필요 없습니다. RxSwift와 Combine은 각각 Disposable과 Cancellable에 대한 참조를 유지해야 하는 것과는 대조적인 부분입니다.

func taskExample() {
    print(Thread.current)
    Task { //👉 참조를 유지하지 않아도 되고 바로 실행됨
        print(Thread.current)
    }
}

취소

Task 객체의 참조를 유지하지 않아도 실행하는데 문제가 없지만 취소하기 위해서는 참조를 유지해야 합니다. 예를 들어 아래와 같이 데이터를 네트워크에서 다운받아서 UI를 업데이트하는 Task가 있다고 하겠습니다.

let downloadTask = Task {
    dataForUI = await downloadData()
}

하지만 더 이상 해당 UI의 업데이트가 필요없다고 할 때는 이 Task를 아래와 같이 취소할 수 있습니다.

downloadTask.cancel()

취소 되었는지 확인

해당 Task가 취소되었는지 아닌지 확인할 때는 isCancelled 속성으로 확인합니다.

downloadTask.isCancelled

Task 블록 안에서는 Task.isCancelled 혹은 Task.checkCancellation()를 활용합니다

let downloadTask = Task {
    try Task.checkCancellation()
    Task.isCancelled
}

Task의 리턴값

Task는 리턴값을 가지는 클로저를 인자로 받을 수 있습니다. 이 리턴값은 .value로 접근할 수 있습니다.

let downloadTask = Task {
    return "다운로드 완료!"
}

print(downloadTask.value)

마치며

이번 포스팅에서는 Task의 정의와 참조와 취소, 리턴값에 대해서 코드 차원에서만 다루었는데요. 다음 포스팅에서는 참조, 취소, 리턴값을 실제 예시 코드와 함께 어떻게 동작하는지 알아보도록 하겠습니다!

profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.

0개의 댓글