Swift线程安全Int变量实现方案:多线程增减+每秒自减至0通知
嘿,这个需求我刚好做过类似的,最优方案应该是结合GCD队列保证线程安全,搭配DispatchSourceTimer实现可靠的自动递减——既解决了多线程竞态问题,又能避免普通Timer的RunLoop依赖坑。下面给你详细拆解实现:
核心实现:线程安全计数器 + 自动递减通知
import Dispatch class ThreadSafeCounter { // 用并发队列+barrier实现读写分离:读取可并发,写入排他,比纯串行队列效率更高 private let syncQueue = DispatchQueue(label: "com.yourapp.ThreadSafeCounter.sync", attributes: .concurrent) private(set) var value: Int = 0 // 自动递减计时器:用DispatchSourceTimer,比NSTimer更可靠,不受RunLoop模式限制 private var timer: DispatchSourceTimer? // 两种通知方式:Block(推荐)和Selector var onZeroTriggered: (() -> Void)? weak var notificationTarget: NSObject? var notificationSelector: Selector? // MARK: - 初始化 init(initialValue: Int = 0) { self.value = initialValue setupAutoDecrementTimer() // 初始值为0时直接触发通知 if value == 0 { triggerZeroNotification() } } // MARK: - 线程安全操作方法 func increment() { syncQueue.async(flags: .barrier) { [weak self] in self?.value += 1 // 从0变回正数时重启计时器 self?.restartTimerIfNeeded() } } func decrement() { syncQueue.async(flags: .barrier) { [weak self] in guard let self = self else { return } self.value -= 1 self.checkAndHandleZeroState() } } func setValue(_ newValue: Int) { syncQueue.async(flags: .barrier) { [weak self] in guard let self = self else { return } self.value = newValue self.checkAndHandleZeroState() } } func getCurrentValue() -> Int { // 读取用同步操作,不阻塞写入队列 return syncQueue.sync { value } } // MARK: - 计时器逻辑 private func setupAutoDecrementTimer() { timer = DispatchSource.makeTimerSource(queue: syncQueue) timer?.schedule(deadline: .now() + 1, repeating: .seconds(1)) timer?.setEventHandler { [weak self] in self?.decrement() } } private func restartTimerIfNeeded() { if value > 0 && timer?.isSuspended == true { timer?.resume() } } private func checkAndHandleZeroState() { if value == 0 { timer?.suspend() triggerZeroNotification() } else if value > 0 && timer?.isSuspended == true { timer?.resume() } } // MARK: - 通知触发 private func triggerZeroNotification() { // 切回主线程触发,避免后台线程执行UI操作 DispatchQueue.main.async { [weak self] in guard let self = self else { return } // 优先触发Block self.onZeroTriggered?() // 再触发Selector(如果设置了) if let target = self.notificationTarget, let selector = self.notificationSelector { target.perform(selector) } } } // MARK: - 内存清理 deinit { timer?.cancel() } }
使用示例
// 1. 创建初始值为5的计数器 let counter = ThreadSafeCounter(initialValue: 5) // 2. 设置Block通知 counter.onZeroTriggered = { print("计数器归零啦!") // 这里可以写UI更新逻辑,比如弹出提示 } // 3. 或者设置Selector通知(适合OC风格调用) counter.notificationTarget = self counter.notificationSelector = #selector(handleCounterZero) // 4. 在任意线程安全操作计数器 DispatchQueue.global().async { counter.increment() // 线程安全递增 } DispatchQueue.global().async { let currentValue = counter.getCurrentValue() // 线程安全读取 print("当前值:\(currentValue)") } // Selector回调实现 @objc func handleCounterZero() { print("Selector收到归零通知") }
为什么这是最优方案?
- 线程安全保障:用GCD并发队列+
barrier实现读写分离,读取操作可以并发执行,写入操作排他执行——比手动用NSLock更简洁,不容易出现锁遗漏或死锁问题,是Swift里推荐的线程同步方式。 - 计时器可靠性:
DispatchSourceTimer基于GCD实现,不依赖RunLoop,即使在后台线程也能稳定运行,不会像NSTimer那样因为主线程RunLoop繁忙而延迟触发。 - 内存安全:全程用
weak self避免循环引用,deinit里主动cancel计时器,防止内存泄漏。 - 灵活扩展:同时支持Block和Selector两种通知方式,还能轻松扩展批量增减、暂停/重启计时器等功能。
注意事项
- 不要直接访问
value变量,必须通过getCurrentValue()、increment()等方法操作,否则会破坏线程安全。 - 如果允许计数器变为负数,需要调整
checkAndHandleZeroState的逻辑,比如只有当值从正数降到0时触发通知。
内容的提问来源于stack exchange,提问作者kandaurov_net
相关产品推荐
相关产品推荐

