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

调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 00:13:13