Mastering Concurrency in iOS - Part 4 (Dispatch Barrier, Semaphore, Work Item Flags)
@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
플래그를 설정한 채 비동기 구문을 작성하면, 내부 태스크를 순차적으로 보장할 수 있기 때문에 데이터 안전성을 보장할 수 있음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()
을 통해 들어올 수 있음을 알려줌