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

取消DispatchWorkItem无法中断含DispatchSemaphore的循环如何解决

问题原因

你的取消逻辑仅在每轮循环的开头检测isCancelled状态,当代码执行到semaphore.wait()时,线程会进入阻塞状态。此时即使调用了cancel(),代码也无法走到下一轮的状态判断逻辑,只能等待当前addSticker的回调触发signal()后才会继续执行,这就是取消不生效的核心原因。


解决方案1:现有代码最小改动(带超时轮询检测取消状态)

把无限等待的semaphore.wait()替换为带超时的轮询等待,每隔一小段时间唤醒一次检查任务是否已被取消,无需等待信号量触发即可响应取消操作:

func performStickerWorkItems(_ stickers: [String]) {
    let queue = DispatchQueue.global(qos: .background)
    workItem = DispatchWorkItem { [weak self] in
        for sticker in stickers {
            guard let item = self?.workItem, !item.isCancelled else {
                print("cancelled")
                break
            }
            let semaphore = DispatchSemaphore(value: 0)
            self?.addSticker(sticker: sticker) {
                print("S: \(sticker)")
                semaphore.signal()
            }
            // 替换原来的 semaphore.wait()
            while true {
                // 每100毫秒唤醒一次检查取消状态,可根据业务场景调整间隔
                let waitResult = semaphore.wait(timeout: .now() + 0.1)
                if waitResult == .success || self?.workItem?.isCancelled == true {
                    break
                }
            }
            // 检测到取消直接终止整个循环
            if self?.workItem?.isCancelled == true {
                break
            }
        }
        
        self?.workItem = nil
    }
    
    queue.async(execute: workItem!)
    workItem?.notify(queue: .main) { [weak self] in
        self?.updateButton(false)
    }
}

改动后最多会有你设置的超时间隔的延迟,就可以响应取消操作,终止后续任务。


解决方案2:更优雅的原生实现(用OperationQueue管理任务)

如果不想自己维护信号量和任务状态,可以使用自带取消能力的OperationQueue管理串行任务,实现更简洁易维护:

// 声明私有属性
private let stickerTaskQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 1 // 串行执行,和原semaphore的顺序执行逻辑一致
    queue.qualityOfService = .background
    return queue
}()

func performStickerWorkItems(_ stickers: [String]) {
    // 先清空旧的未执行任务
    stickerTaskQueue.cancelAllOperations()
    
    for sticker in stickers {
        let task = BlockOperation { [weak self] in
            guard let self = self, !task.isCancelled else { return }
            let semaphore = DispatchSemaphore(value: 0)
            self.addSticker(sticker: sticker) {
                guard !task.isCancelled else { return }
                print("S: \(sticker)")
                semaphore.signal()
            }
            // 可加最大超时防止单个任务异常导致永久阻塞
            _ = semaphore.wait(timeout: .now() + 30)
        }
        stickerTaskQueue.addOperation(task)
    }
    
    // 所有任务完成后的回调
    stickerTaskQueue.addBarrierBlock { [weak self] in
        DispatchQueue.main.async {
            self?.updateButton(false)
        }
    }
}

@IBAction func backWasPressed(_ sender: Any) {
    stickerTaskQueue.cancelAllOperations() // 直接取消所有未执行、执行中的任务
    navigationController?.popViewController(animated: true)
}

内容的提问来源于stack exchange,提问作者Bhavin Bhadani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 19:18:03