同时填充数组

时间:2019-03-16 10:43:08

标签: swift memory memory-management grand-central-dispatch swift5

我在Swift 5中遇到了并发和数组问题。为了重现该问题,我将代码简化为以下片段:

import Dispatch

let group = DispatchGroup()
let queue = DispatchQueue(
  label: "Concurrent threads",
  qos: .userInitiated,
  attributes: .concurrent
)

let threadCount = 4
let size = 1_000
var pixels = [SIMD3<Float>](
  repeating: .init(repeating: 0),
  count: threadCount*size
)

for thread in 0..<threadCount {
  queue.async(group: group) {
    for number in thread*size ..< (thread+1)*size {
      let floating = Float(number)
      pixels[number] = SIMD3<Float>(floating, floating, floating)
    }
  }
}

print("waiting")
group.wait()
print("Finished")

当我使用Xcode版本10.2 beta 4(10P107d)在调试模式下执行此操作时,它总是崩溃,并显示以下错误:

Multithread(15095,0x700008d63000) malloc: *** error for object 0x104812200: pointer being freed was not allocated
Multithread(15095,0x700008d63000) malloc: *** set a breakpoint in malloc_error_break to debug

我感觉这是编译器中的一些错误,因为当我在发布模式下运行代码时,它运行得很好。还是我在这里做错了什么?

1 个答案:

答案 0 :(得分:3)

数组中的指针绝对可以在您的脚下改变。它不是原始内存。

数组不是线程安全的。数组是值类型,这意味着它们以线程安全的方式支持写时复制(因此您可以将数组自由传递给另一个线程,如果将它复制到另一个线程中也可以),但是您不能在多个线程上变异相同的数组。数组不是C缓冲区。它不保证有连续的内存。甚至根本没有承诺要分配内存。数组可以在内部选择将“我现在全零”存储为特殊状态,并且对每个下标仅返回0。 (不是,但是允许。)

对于这个特定的问题,您通常会使用vDSP方法,例如vDSP_vramp,但我知道这只是一个示例,可能没有解决该问题的vDSP方法。不过,通常情况下,我仍然会专注于Accelerate / SIMD方法,而不是分派到队列。

但是,如果要分派到队列,则需要UnsafeMutableBuffer来控制内存(并确保内存甚至存在):

pixels.withUnsafeMutableBufferPointer { pixelsPtr in
    DispatchQueue.concurrentPerform(iterations: threadCount) { thread in
        for number in thread*size ..< (thread+1)*size {
            let floating = Float(number)
            pixelsPtr[number] = SIMD3(floating, floating, floating)
        }
    }
}

“不安全”表示现在要确保所有访问权限都是合法的,并且您没有在创建争用条件。

请注意此处使用.concurrentPerform。正如@ user3441734提醒我们的那样,pixelsPtr一旦完成,就不会保证.withUnsafeMutablePointer是有效的。保证.concurrentPerform直到所有块都完成才返回,因此保证指针是有效的。

这也可以通过DispatchGroup完成,但是.wait必须位于withUnsafeMutableBufferPointer内。