调用DispatchQueue sync执行读取操作是否安全?
串行DispatchQueue读写字典的线程安全与风险分析
问题描述
我有一个通过串行DispatchQueue对字典进行读写操作的类:写入操作采用异步方式,读取操作采用同步方式。我希望避免在读取时使用完成处理程序,想了解这种实现是否会引发问题,比如死锁或在主线程调用时崩溃?
public final class UserRegistry { private var infoMap: [String: User] = [:] private let queue: DispatchQueue = .init(label: "userRegistry.queue") func register(_ user: User) { queue.async { [weak self] in self?.infoMap[user.id] = user } } func deregister(_ user: User) { queue.async { [weak self] in self?.infoMap[user.id] = nil } } func getUser(for id: String) -> User? { queue.sync { infoMap[id] } } }
我尝试过使用完成处理程序,但这会导致整个代码库中的调用点都需要修改。我期望同步读取操作无论从哪个线程调用都是安全的。
- 读取操作:
func getUser(for id: String) -> User? - 写入操作:
func register(_ user: User)和func deregister(_ user: User)
回答
核心结论
你的实现不会触发死锁或主线程崩溃,且能保证线程安全,但有几个细节需要注意:
1. 死锁的唯一触发场景
串行队列的sync调用只有在当前线程就是这个私有队列本身时才会死锁。比如如果有人在register/deregister的异步闭包里调用getUser,此时串行队列正在执行闭包任务,sync会等待队列空闲,但队列被当前任务占用,永远无法完成,就会导致死锁。但只要外部调用getUser的线程不是这个私有队列,就完全安全。
2. 主线程调用的影响
在主线程调用getUser时,主线程会短暂阻塞,等待私有队列返回结果,但不会崩溃。如果你的写入操作不频繁、数据量小,这种阻塞基本感知不到;但如果写入操作很密集,主线程阻塞可能会导致UI卡顿。
3. 线程安全的有效性
所有读写操作都通过同一个串行队列执行,天然保证了操作的顺序性:
- 写入异步执行,不阻塞调用线程
- 读取同步执行,能拿到最新的完整数据
- 不会出现读写同时进行的竞态条件,完全符合线程安全的要求
4. 可选优化
如果担心主线程阻塞问题,可以给私有队列设置更高的服务质量等级,让读取任务优先执行:
private let queue: DispatchQueue = .init(label: "userRegistry.queue", qos: .userInitiated)
内容的提问来源于stack exchange,提问作者DesperateLearner
相关产品推荐
相关产品推荐

