Swift 值类型在多线程中真的安全吗?

Swift 值类型在多线程中真的安全吗?,swift,concurrency,Swift,Concurrency,苹果说,“如果你使用Value-type,你可以安全地在线程间传递值的副本,而无需同步。”。但我最近看到并发崩溃,与苹果指南不符 我看到和苹果的指南说“值类型在多线程中是安全的”,所以我认为“值类型是原子的!”但最近我看到下面代码中并发崩溃 class ClassB: NSObject { func readSomeValue() { print(classA.someValue) } let classA = ClassA() } class ClassA

苹果说,“如果你使用Value-type,你可以安全地在线程间传递值的副本,而无需同步。”。但我最近看到并发崩溃,与苹果指南不符

我看到和苹果的指南说“值类型在多线程中是安全的”,所以我认为“值类型是原子的!”但最近我看到下面代码中并发崩溃

class ClassB: NSObject {

   func readSomeValue() {
      print(classA.someValue)
   }

   let classA = ClassA()

}

class ClassA: NSObject {

  private(set) var someValue: StructA? {
    didSet{
      if oldValue != self.someValue { self.someFunction(self.someValue) }
    }
  }

  private func modifySomeValue(_ newValue: StructA) {
    self.someValue = newValue
  }
}

struct StructA {
  var a: Double
  var b: String?
}
在thread1中执行readSomeValue()并在thread2中执行modifySomeValue()时发生并发崩溃。
为什么并发崩溃会发生?值类型在多线程中不安全吗?

有几个观察结果:

  • 也就是说:

    重要的是,您可以安全地跨线程传递值的副本,而无需同步

    这里的关键词是“副本”

    但在您的示例中,您没有将值类型对象的副本传递给不同的线程。您在线程之间共享引用类型对象的单个实例,一个
    类。当然,您的引用类型有一个value-type属性,但这不会改变您正在跨线程共享引用类型对象实例的单个实例的事实。为了享受线程安全,您必须手动同步与该对象及其属性的交互

  • 有一种观点认为,许多讨论误导了读者,使他们认为Swift值类型总是喜欢复制(或写时复制)语义,因此总是喜欢这种线程安全特性。但您必须小心,因为有几个示例无法获得复制语义。在引用类型对象中具有值类型属性的示例就是一个示例

    另一个例子是当您未能使用闭包时。例如,以下内容不是线程安全的,因为它跨多个线程使用相同的值类型实例:

    var object = StructA(a: 42, b: "foo")
    DispatchQueue.global().async {
        print(object)
    }
    object.b = "bar"
    
    但通过添加捕获列表,全局队列将拥有自己的对象副本,从而恢复线程间的线程安全交互,因为每个线程都有自己的对象副本:

    var object = StructA(a: 42, b: "foo")
    DispatchQueue.global().async { [object] in
        print(object)
    }
    object.b = "bar"
    
  • 是的,如果您(a)使用值类型,您可以编写线程安全代码;和(b)传递这些值类型的副本。但这与原子性无关。总之,Swift变量不是原子变量