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

Swift中如何在concurrent enumerateObjects并发块中填充字典?

问题解答

直接结论

不可以直接在enumerateObjects(options: .concurrent)的并发执行块内对同一个字典/数组做写入操作。Swift 中的Dictionary、Array等原生集合类型默认都不是线程安全的,多个线程同时对同一个集合做写入操作时会发生内存竞争,你遇到的EXC_BAD_ACCESS就是典型的并发写导致的内存访问错误。
直接每次写入前加信号量/锁的方式会让所有写入操作退化成串行执行,完全抵消并发的性能收益,不符合性能优化的需求。

崩溃示例截图

高性能解决方案

核心思路是避免多线程同时操作同一个字典,每个线程生成自己的局部字典,所有并发任务执行完成后再合并所有局部字典到最终结果里。全程只有将局部字典加入全局数组的阶段需要低频率加锁,几乎没有额外开销,能最大化利用并发性能。

优化后的示例代码如下:

struct Coordinate: Hashable {
    let x: Int
    let y: Int
}

struct CoordColor: Hashable{
    let value: UIColor
}

func parseImgColor(_ cgImage: CGImage) -> [Coordinate : CoordColor] {
    let height = cgImage.height
    let width = cgImage.width
    
    var partialMaps: [[Coordinate : CoordColor]] = []
    let lock = NSLock()
    
    // 用GCD的concurrentPerform替代NSArray的enumerateObjects,更符合Swift规范,性能更优
    DispatchQueue.concurrentPerform(iterations: height) { y in
        var localMap: [Coordinate : CoordColor] = [:]
        // 内层循环直接串行遍历即可,不需要嵌套并发避免额外调度开销
        for x in 0..<width {
            let coords = Coordinate(x: x, y: y)
            localMap[coords] = CoordColor(value: x % 2 == 0 ? .blue : .white)
        }
        // 仅在提交局部结果时加锁,加锁频率等于图片高度,和像素总数无关,开销可忽略
        lock.lock()
        partialMaps.append(localMap)
        lock.unlock()
    }
    
    // 串行合并所有局部字典得到最终结果
    var finalMap: [Coordinate : CoordColor] = [:]
    partialMaps.forEach { finalMap.merge($0) { (current, _) in current } }
    return finalMap
}

方案优势

  • 每个并发线程仅操作自己的局部字典,99%的执行逻辑不需要加锁,完全保留并发的性能收益
  • 加锁操作仅在每个行处理完成后执行一次,频率极低,不会成为性能瓶颈
  • 去掉了原来的NSArray桥接转换逻辑,减少了不必要的类型转换开销
  • 去掉了嵌套的双层并发逻辑,避免了多余的线程调度开销,单层级并发已经可以拉满CPU所有核心的利用率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 08:15:06