[UIKit] Concurrency: Dispatch Barrier & Semaphore & Work Item Flags

Junyoung Park·2022년 12월 26일
0

UIKit

목록 보기
136/142
post-thumbnail

Mastering Concurrency in iOS - Part 4 (Dispatch Barrier, Semaphore, Work Item Flags)

Concurrency: Dispatch Barrier & Semaphore & Work Item Flags

DispatchWorkItemFlags

  • 워크 아이템을 모아놓은 집합
  • 플래그: 여섯 개의 플래그를 통해 QoS 조정, 이중 배리어가 컨커런시와 관련성이 높음
  • barrier: 레이스 컨디션을 피하거나 태스크 타이밍을 컨트롤하고자 할 때 사용 가능한 플래그
  • 컨커런트 큐에 특정한 워크 아이템이 들어갔을 때 일종의 배리어 역할을 하도록 하는 플래그
@objc private func didTapCombo() {
        for item in items {
            self.purchaseQueue.async(flags: .barrier) { [weak self] in
                guard let self = self else { return }
                self.addItems(item: item)
            }
        }
    }
    
    private func addItems(item: ItemModel) {
        if walletBalance >= item.price {
            PurchaseManager.shared.buyItem(item: item, balance: walletBalance) { [weak self] success in
                guard let self = self else { return }
                if success {
                    DispatchQueue.main.async {
                        self.walletBalance -= item.price
                        self.cartBalance += item.price
                    }
                }
            }
        }
    }
  • 레이스 컨디션을 해결하기 위해 커스텀 컨커런트 큐에 barriers 플래그를 설정한 채 비동기 구문을 작성하면, 내부 태스크를 순차적으로 보장할 수 있기 때문에 데이터 안전성을 보장할 수 있음

Dispatch Semaphore

  • 디스패치 배리어를 통해 해결 가능한 레이스 문제를 세마포어로도 해결 가능

Critical Section

  • 공유 리소스에 접근하기 위한 구역
  • 크리티컬 섹션에 멀티 스레드가 동시에 접근하게 되면 데이터 안전성에 해가 될 확률이 높음
  • 배타적 접근을 통해 data inconsistency를 해결 가능 → 세마포어
  • 디스패치 배리어 → 해당 태스크 실행 중 다른 태스크 실행을 막는 배타적 역할
  • 세마포어 → 구현 방식에 따라 몇 개의 스레드를 실행할 것인지 모두 결정 가능. counter 값을 몇 개로 설정하느냐에 따라서 스레드 큐의 가용 개수를 설정 가능
  • 크리티컬 섹션에 존재하는 스레드 개수에 따라서 counter 값이 변경, 해당 크리티컬 섹션으로 들어가기 전 단계에 wait(), signal() 메소드를 통해 스레드의 행동을 컨트롤
    private let semaphore = DispatchSemaphore(value: 1)
  • 커스텀 세마포어를 카운터 개수를 설정한 채로 구현
@objc private func didTapCombo() {
        items.forEach { [weak self] item in
            self?.purchaseQueue.async {
                self?.addItems(item: item)
            }
        }
    }
  • 별도의 배리어 플래그를 설정하지 않은 채로 레이스 문제를 유발할 수 있는 구현 방식으로 여러 개의 비동기 함수를 동시 실행
private func addItems(item: ItemModel) {
        semaphore.wait()
        if walletBalance >= item.price {
            PurchaseManager.shared.buyItem(item: item, balance: walletBalance) { [weak self] success in
                guard let self = self else { return }
                if success {
                    DispatchQueue.main.async {
                        self.walletBalance -= item.price
                        self.cartBalance += item.price
                        self.semaphore.signal()
                    }
                }
            }
        }
    }
  • wait()를 통해 카운터 값에 맞춰 크리티컬 섹션에 들어갈 수 있는 스레드 개수를 한정, signal()을 통해 들어올 수 있음을 알려줌
profile
JUST DO IT

0개의 댓글