You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

访问OperationQueue的operationCount属性时应用崩溃求助

解决OperationQueue访问operationCount时的偶发崩溃问题

嘿,这个问题我之前踩过坑!咱们来一步步拆解看看👇

崩溃的核心原因

首先得明确:OperationQueue的operationCount属性并不是线程安全的。苹果的文档里虽然没明说,但队列在后台添加、执行、移除操作时,内部的操作列表是在动态变化的。如果此时你在其他线程(比如主线程)直接访问operationCount,就会触发竞态条件——两个线程同时读写队列的内部数据结构,最终导致崩溃。

你的场景里,parallelOperationQueue是后台优先级的队列,它的操作在后台线程跑,而你在parallelOperations(for:)方法里直接访问operationCount,刚好撞上队列内部修改的时机,就会出现偶发崩溃。

可行的解决方案

方案1:给operationCount加线程安全的访问包装

用一个串行DispatchQueue来保护对operationCount的访问,确保每次读取都是原子操作:

// 新增一个串行锁队列,专门保护对parallelOperationQueue的访问
private let queueAccessLock = DispatchQueue(label: "com.yourApp.operationQueueLock")
private var parallelOperationQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.qualityOfService = .background
    return queue
}()

// 封装一个线程安全的操作数量获取方法
private var safeOperationCount: Int {
    queueAccessLock.sync {
        parallelOperationQueue.operationCount
    }
}

// 修改你的方法,用safeOperationCount代替直接访问
private func parallelOperations(for tokens: [TokenObject]) {
    guard safeOperationCount == 0, !tokens.isEmpty else { return }
    // 后续逻辑...
}

方案2:自己维护线程安全的操作计数器(更可靠)

如果你的业务逻辑依赖队列是否为空,其实完全可以自己跟踪操作的生命周期,避免依赖系统提供的operationCount。这样不仅线程安全,还能避免系统属性的不确定性:

private let countLock = NSLock()
private var activeOperationCount = 0
private var parallelOperationQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.qualityOfService = .background
    return queue
}()

// 封装添加操作的方法,自动维护计数器
private func addSafeOperation(_ operation: Operation) {
    countLock.lock()
    activeOperationCount += 1
    countLock.unlock()
    
    // 给操作添加完成回调,计数器减1
    operation.completionBlock = { [weak self] in
        guard let self = self else { return }
        self.countLock.lock()
        self.activeOperationCount -= 1
        self.countLock.unlock()
    }
    
    parallelOperationQueue.addOperation(operation)
}

// 修改你的判断逻辑
private func parallelOperations(for tokens: [TokenObject]) {
    countLock.lock()
    let isQueueIdle = activeOperationCount == 0
    countLock.unlock()
    
    guard isQueueIdle, !tokens.isEmpty else { return }
    // 后续逻辑...
}

方案3:用DispatchGroup跟踪操作完成(适合批量操作场景)

如果你的场景是要等一批操作全部完成后再执行新任务,用DispatchGroup会更直观:

private let operationGroup = DispatchGroup()
private var parallelOperationQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.qualityOfService = .background
    return queue
}()

// 添加操作时进入group
private func addGroupOperation(_ operation: Operation) {
    operationGroup.enter()
    operation.completionBlock = {
        self.operationGroup.leave()
    }
    parallelOperationQueue.addOperation(operation)
}

// 等待所有操作完成(注意:不要在主线程调用wait方法!)
private func waitForAllOperations() {
    operationGroup.wait()
    // 所有操作完成后的逻辑
}

额外提醒

尽量不要依赖operationCount做业务判断,因为它的返回值是瞬时状态——哪怕你刚读到0,下一秒可能就有其他线程往队列里加了新操作。自己维护状态或者用DispatchGroup、Operation依赖,会让逻辑更可靠。

内容的提问来源于stack exchange,提问作者Oleg Gordiichuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:14:26