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

使用DispatchQueue.global().sync方法时出现数据竞争崩溃问题求助

关于Set线程安全的问题分析与解决

你当前的写法存在线程安全隐患,问题出在这两点:

  • 你用的DispatchQueue.global()是并发队列,即使通过sync执行remove操作,也只能保证这个remove任务在队列里执行,但如果其他线程直接修改fetchingInProgressList,或者用其他队列操作这个集合,依然会出现多线程并发访问的冲突。
  • 线程安全的核心是所有对共享变量的读写操作都必须串行化,也就是同一时间只能有一个线程操作它,而你只给remove加了队列包裹,其他操作如果没做同样处理,照样不安全。

正确的实现方式

推荐用串行队列来包裹所有对fetchingInProgressList的操作,示例代码如下:

// 创建专门用于访问共享集合的串行队列
private let accessQueue = DispatchQueue(label: "com.yourapp.fetchingListAccess")
private var fetchingInProgressList: Set<String> = []

// 添加元素
func addFetchingTask(_ taskId: String) {
    accessQueue.async {
        self.fetchingInProgressList.insert(taskId)
    }
}

// 删除元素
func removeFetchingTask(_ taskId: String) {
    accessQueue.sync {
        _ = self.fetchingInProgressList.remove(taskId)
    }
}

// 查询是否在执行中
func isTaskFetching(_ taskId: String) -> Bool {
    return accessQueue.sync {
        self.fetchingInProgressList.contains(taskId)
    }
}

补充说明

如果追求更高的读写效率,也可以用并发队列配合barrier:写操作(insert/remove)用async(flags: .barrier),读操作直接用async或sync,这样多个读操作可以并发执行,写操作会等待所有读完成后执行,且写过程中不会有其他读写操作干扰。示例如下:

private let accessQueue = DispatchQueue(label: "com.yourapp.fetchingListAccess", attributes: .concurrent)
private var fetchingInProgressList: Set<String> = []

// 写操作(用barrier保证串行)
func addFetchingTask(_ taskId: String) {
    accessQueue.async(flags: .barrier) {
        self.fetchingInProgressList.insert(taskId)
    }
}

func removeFetchingTask(_ taskId: String) {
    accessQueue.async(flags: .barrier) {
        _ = self.fetchingInProgressList.remove(taskId)
    }
}

// 读操作(可并发)
func isTaskFetching(_ taskId: String) -> Bool {
    return accessQueue.sync {
        self.fetchingInProgressList.contains(taskId)
    }
}

核心原则就是:所有对共享可变变量的操作,都必须通过同一个同步机制来管控,不能有任何直接访问的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 20:35:02