【问题标题】:Swift array copy on write in a multithreaded environment在多线程环境中写入时的 Swift 数组复制
【发布时间】:2016-11-10 11:33:27
【问题描述】:

Swift 数组是在写入时复制的值类型。如果原始数组未发生变异,则“副本”指向相同的内存位置。

假设我们有一个被多个线程引用的类

class Foo {
    var numbers: [Int] = [1, 2, 3]
}
let foo = Foo()

如果线程 A“复制”numbers

var numbers = foo.numbers

然后线程 B 用不同的数组实例替换 numbers

foo.numbers = [4, 5, 6]

当线程 B 尝试访问其元素时,原始数字数组 ([1, 2, 3]) 是否会被释放,从而导致 incorrect checksum for freed object - object was probably modified after being freed

【问题讨论】:

  • 您收到哪个对象的错误/警告?
  • 我非常不喜欢 Swift 数组通过值而不是指针传递的方式。这已经让我晾干了好几次了。

标签: ios swift


【解决方案1】:

var numbers = foo.numbers 将始终包含 [1, 2, 3],直到您修改 var numbers

let foo = Foo()
// Thread A
var numbers = foo.numbers

// Thread b
foo.numbers = [4, 5, 6]

// Thread A again - 'var numbers' still has a "reference" to [1, 2, 3]
print(numbers) // "[1, 2, 3]"

我认为您误解了 swift 中的结构如何充当值类型。重新分配 foo.numbers 时不会覆盖原始的 [1, 2, 3] 数组。

示例

假设你有 10 个线程全部“复制”foo.numbers,此时数组的内存没有被复制。

var myTheadNumbers = foo.numbers

假设线程 1 修改了数组

myThreadNumbers.append(4)

[1, 2, 3] 被复制到一个新数组中,然后通过附加 4 对其进行修改。

线程 1 现在拥有一个 [1, 2, 3, 4] 数组,而线程 2-10 仍然共享原始的 [1, 2, 3]。这就是写时复制对结构的意义——只有当您修改结构并且其他人有旧副本时,复制的性能才会受到影响。

这 10 个线程中的每一个都像拥有自己的 [1, 2, 3] 副本一样,这就是值类型的含义。

【讨论】:

    【解决方案2】:

    首先,考虑一下没有并发会发生什么:

    在所有强引用被删除之前,原始数组不会被释放,所以只要numbers 仍然持有引用,原始数组就会在内存中。

    当您将一个新数组分配给foo.numbers 时,您会删除一个对原始数组的引用,但numbers 仍然拥有一个引用,因此它不会被释放。 foo.numbers 现在拥有对新数组的强引用。

    即使我在原地修改foo.numbers

    foo.numbers.append(4)
    

    numbers 仍将保留对原始数组的引用,foo.numbers 现在将保留对新数组的引用,因为它在修改时被复制。

    一旦将并发性引入所有这些,它就会变得一团糟,因为这些复制和赋值操作可能不是原子的。任何对象的并发更新都需要保护关键部分以防止损坏。

    您可以使用串行调度队列来管理对 numbers 数组的任何更新。

    class Foo {
    
        private var _numbers: [Int] = [1,2,3];
    
        private let serialQ = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL)
    
        var numbers: [Int] {
            set {
                dispatch_sync(self.serialQ) { 
                    self._numbers = newValue
                }
            }
            get {
                return self._numbers
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-11
      相关资源
      最近更新 更多