[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🍎 Apple/Concurrency & GCD

[Concurrency] Task Group의 동시성을 제한하는 방법

inu 2023. 11. 13. 21:56
반응형

참고자료 : WWDC23 Beyond the basics of structured concurrency


Task Group의 동시성을 제한하는 방법

  • chopIngredients는 여러 재료(ingredients)를 받아 그들을 하나씩 써는(chop) 메서드이다.
  • 하지만 정말 하나씩 재료를 썬다면 이는 비효율적일 것이다. 동시에 처리하고 싶다.
  • Task Group을 사용하여 Structed Concurrency를 구성하고 하위 Task를 여러개 추가해 처리한다. 그리고 하위 Task가 모두 완료되었을 때 요청한 chopIngredients Task가 종료된다.

다만 여기서 주의할 점은 파라미터로 제공하는 재료들(ingredients)이 너무 많을 경우 각각에 대해 실행 Context를 생성하고 우리는 모든 리소스를 chop하는데 사용해버릴 것이다. (컴퓨터적으로 바라보았을 때는 모든 스레드를 해당 작업 처리에 사용하는 것이고, 실상황을 가정했을 때는 모든 요리사들이 재료를 써는데 투입된다고 가정할 수 있겠다. 모두 상황 모두 좋지 못한 상황임을 인지할 수 있을 것이다.)

이를 방지하기 위해 동시에 써는 재료의 개수를 제한해보자.

func chopIngredients(_ ingredients: [any Ingredient]) async -> [any ChoppedIngredient] {
    return await withTaskGroup(of: (ChoppedIngredient?).self,
                               returning: [any ChoppedIngredient].self) { group in
        // Concurrently chop ingredients
        let maxChopTasks = min(3, ingredients.count)
        for ingredientIndex in 0..<maxChopTasks {
            group.addTask { await chop(ingredients[ingredientIndex]) }
        }

        // Collect chopped vegetables
        var choppedIngredients: [any ChoppedIngredient] = []
        var nextIngredientIndex = maxChopTasks
        for await choppedIngredient in group {
            if nextIngredientIndex < ingredients.count {
                group.addTask { await chop(ingredients[nextIngredientIndex]) }
                nextIngredientIndex += 1
            }
            if let choppedIngredient {
                choppedIngredients.append(choppedIngredient)
            }
        }
        return choppedIngredients
    }
}
  • 먼저 동시에 처리하도록 만들 개수를 정한다 (maxChopTasks) 그만큼만 우선 group에 추가해두고, 결과를 받아오는 쪽 코드로 넘어간다.
  • 그 후 작업이 하나씩 완료될때마다 아직 마무리하지 못한 작업을 group에 추가한다.
  • 이렇게 하면 작업의 최대개수만큼만 동시 작업을 유지하고 주어진 ingredient를 chop할 수 있게 된다.
withTaskGroup(of: Something.self) { group in
    for _ in 0..<maxConcurrentTasks {
        group.addTask { }
    }
    while let <partial result> = await group.next() {
        if !shouldStop { 
            group.addTask { }
        }
    }
}

일반화된 코드는 위와 같다.

반응형

'🍎 Apple > Concurrency & GCD' 카테고리의 다른 글

[Concurrency] Explore structured concurrency in Swift  (1) 2023.10.29
[Concurrency] Actor  (0) 2023.03.31
[Concurrency] Continuation  (2) 2023.03.31
[Concurrency] Task  (0) 2023.03.31
[Concurrency] async / await  (2) 2023.03.31