3-8.【函数式编程】比较使用 struct 不可变数组和 class 可变数组在并发环境下的安全性。

0 阅读2分钟

1️⃣ 核心区别

特性struct + let(不可变数组)class + var(可变数组)
类型值类型引用类型
默认行为赋值/传递会复制,修改生成新副本赋值/传递是引用,共享同一对象
可变性不可变,无法修改内容可变,可随时修改
线程安全默认线程安全(只读)默认不线程安全,需要加锁
性能大型结构可能复制开销,但 Swift 做了优化(Copy-on-Write)修改无需复制,性能高,但要处理竞争条件

2️⃣ struct 不可变数组在并发环境

let numbers: [Int] = [1, 2, 3, 4]

// 并发访问
DispatchQueue.concurrentPerform(iterations: 4) { i in
    print(numbers[i])  // 只读访问,没有副作用
}

特点

  • 数组是 let,不可修改 → 线程安全

  • Swift 的值类型在传递和赋值时会做 Copy-on-Write

    • 当没有修改时,不会复制,性能开销小
    • 修改时才生成副本 → 并发读取仍然安全

结论:只读访问的 struct 不需要额外同步机制。


3️⃣ class 可变数组在并发环境

class NumberHolder {
    var numbers: [Int] = [1, 2, 3, 4]
}

let holder = NumberHolder()

// 并发修改
DispatchQueue.concurrentPerform(iterations: 4) { i in
    holder.numbers.append(i) // ❌ 竞态条件
}

问题

  • 数组是引用类型,多个线程共享同一对象

  • 并发写操作会导致:

    • 数据竞争(race condition)
    • 崩溃或数据损坏

解决方法

  1. 使用 DispatchQueue 串行访问:
let queue = DispatchQueue(label: "sync.queue")
queue.async {
    holder.numbers.append(5)
}
  1. 使用 NSLock / DispatchSemaphore 保护访问:
let lock = NSLock()
DispatchQueue.global().async {
    lock.lock()
    holder.numbers.append(10)
    lock.unlock()
}

4️⃣ 总结对比

特性struct + let(不可变数组)class + var(可变数组)
读操作多线程安全多线程安全
写操作不可写,无需同步多线程不安全,必须同步
性能Copy-on-Write,有时会复制写操作直接修改,无复制,但加锁影响性能
适用场景并发读取、函数式风格、数据不可变需要共享可变状态,或高性能修改

💡 经验法则

并发环境下,如果可以使用 struct + let → 优先选择不可变数组
避免共享可变状态带来的复杂性和 bug。