Task.yield()

Zion·2023년 6월 6일
1

출처 : https://www.hackingwithswift.com/quick-start/concurrency/how-to-voluntarily-suspend-a-task

func factors(for number: Int) async -> [Int] {
    var result = [Int]()

    for check in 1...number {
        if number.isMultiple(of: check) {
            result.append(check)
        }
    }

    return result
}

let factors = await factors(for: 120)
print("Found \(factors.count) factors for 120.")

굳이 ? await를 써야해?라는 생각이 드는 코드일수도 있지만 Task.yield 설명을 위한 코드이다.

설명 : 매우 비효율적인 구현임에도 불구하고 릴리스 빌드에서는 100,000,000과 같은 숫자에 대해서도 여전히 매우 빠르게 실행됩니다. 하지만 이보다 더 큰 규모의 작업을 시도하면 수억 개의 검사를 실행하는 데 많은 CPU 시간이 소모되어 다른 작업이 조금도 진전되지 못한 채 방치될 수 있다는 것을 알 수 있습니다.

다른 작업은 일부 작업을 시작했다가 네트워크 요청과 같이 즉시 중단할 수 있다는 점을 염두에 두세요. 간단한 개선 방법은 Swift가 원할 경우 다른 작업을 실행할 수 있도록 factors() 메서드를 자주 일시 중지하도록 강제하는 것입니다. 이는 사실상 다른 작업을 실행할 수 있도록 잠시 멈추라고 요청하는 것과 같습니다.

다른 작업은 일부 작업을 시작했다가 네트워크 요청과 같이 즉시 중단할 수 있다는 점을 염두에 두세요. 간단한 개선 방법은 Swift가 원할 경우 다른 작업을 실행할 수 있도록 factors() 메서드를 자주 일시 중지하도록 강제하는 것입니다. 이는 사실상 다른 작업을 실행할 수 있도록 잠시 멈추라고 요청하는 것과 같습니다.

따라서 다음과 같이 100,000개의 숫자마다 Task.yield()를 호출하도록 함수를 수정할 수 있습니다.

func factors(for number: Int) async -> [Int] {
    var result = [Int]()

    for check in 1...number {   
        if number.isMultiple(of: check) {
            result.append(check)
            await Task.yield()                
        }
    }

    return result
}

let factors = await factors(for: 120)
print("Found \(factors.count) factors for 120.")

이렇게 하면 Swift는 배수가 발견될 때마다 일시 중지할 수 있습니다. 예, 처음 몇 번의 반복에서는 많이 호출되지만 시간이 지남에 따라 더 적은 수의 배수가 발견되므로 이전 예제만큼 자주 반환되지 않을 수 있으며, 이는 애초에 yield()를 사용하는 목적을 무색하게 만들 수 있습니다.

yield()를 호출한다고 해서 항상 작업 실행이 중단되는 것은 아닙니다. 대기 중인 다른 작업보다 우선순위가 높은 경우 작업이 즉시 작업을 재개할 수도 있습니다. 강제적으로 다른 작업을 실행하는 것이 아니라 일시적으로 다른 작업을 실행할 수 있는 기회를 Swift에 제공하는 것입니다.

Task.yield()를 호출하는 것은 가상의 Task.doNothing() 메서드를 호출하는 것과 같다고 생각하면 됩니다. 실제로 작업을 생성하지 않고도 Swift가 작업 실행을 조정할 수 있는 기회를 제공하는 것입니다.

profile
어제보다만 나아지는

0개의 댓글