Swift实现线程安全Deque遇问题:size()异常及实现方案咨询
线程安全Deque实现的问题分析与修正
原实现的核心问题
- 异步调用导致返回值未更新:你用
mQueue.async(flags: .barrier)执行闭包,这是异步操作——调用线程不会等待闭包执行完成,直接返回初始值(比如size()里的ret初始为0,闭包还没赋值就返回了),这就是size()始终返回0的根本原因。 - 线程不安全的前置判断:
pushBack里先在无锁状态下读取mJobFifo.count,再异步加锁写入,这个判断和写入不是原子操作,多线程下可能出现多个线程同时判断count < maxSize,然后都执行append,导致队列超出最大容量。 - popFirst无法正确返回元素:同样因为
async异步特性,返回的ret还是初始的nil,闭包里的赋值逻辑还没执行就返回了结果。
正确的实现方式(基于GCD并发队列)
GCD并发队列实现线程安全容器的标准姿势是:
- 读操作:用同步(
sync)普通任务,支持多个读请求并发执行,不阻塞彼此。 - 写/修改操作:用同步(
sync)屏障任务,确保执行时队列内无其他任务,独占访问资源。
修改后的完整代码:
import Foundation import DequeModule enum QueueError: Error { case full } class OcrJob { static var lastJobId: Int = 0 let jobId: Int init() { jobId = OcrJob.lastJobId OcrJob.lastJobId += 1 } } class OcrJobQueue { private let maxSize: UInt private let queue = DispatchQueue(label: "JobQueue", attributes: .concurrent) private var jobFifo: Deque<OcrJob> = [] init(max: UInt) { self.maxSize = max } public func size() -> Int { // 读操作:同步执行,无需屏障,支持并发读 return queue.sync { let count = jobFifo.count print("internal size: \(count)") return count } } public func pushBack(job: OcrJob) throws { // 写操作:同步屏障任务,确保判断与写入的原子性 try queue.sync(flags: .barrier) { guard jobFifo.count < maxSize else { print("pushBack - curSize: \(jobFifo.count), maxSize \(maxSize)") throw QueueError.full } jobFifo.append(job) print("pushBack: \(jobFifo.count) jobs in queue") } } public func popFirst() -> OcrJob? { // 修改操作:同步屏障任务,确保弹出操作的原子性 return queue.sync(flags: .barrier) { let job = jobFifo.popFirst() print("popFirst: \(jobFifo.count) jobs in queue") return job } } }
关键修改点说明
- 将所有
async替换为sync:确保调用线程等待闭包执行完成,拿到正确的返回值。 - 把
pushBack的容量检查移到屏障闭包内:保证判断与写入是原子操作,避免多线程下的容量溢出。 - 补全
QueueError定义:原代码未声明该枚举,会导致编译报错。 - 优化变量命名:符合Swift代码风格,提升可读性。
是否为最优方案?
基于GCD并发队列的实现是macOS平台下线程安全容器的轻量高效方案,完全适配你的服务器线程池任务场景。如果后续需要等待队列非空/非满的阻塞逻辑,可以结合DispatchSemaphore扩展,但基础线程安全Deque用该方案足够。
内容的提问来源于stack exchange,提问作者Danny
相关产品推荐
相关产品推荐

