同时填充数组 [英] Filling an array concurrently

查看:87
本文介绍了同时填充数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am stumbling upon an issue with concurrency and arrays in Swift 5. To reproduce the issue I have simplified my code to the following fragment:

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)在调试模式下执行此操作时,它总是崩溃,并显示以下错误:

When I execute this in debug mode using Xcode Version 10.2 beta 4 (10P107d) it always crashes with an error like:

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

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

I have the feeling that this is some bug in the compiler because when I run the code in release mode it runs just fine. Or am I doing something wrong here?

推荐答案

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

There are pointers inside an Array that absolutely can change under your feet. It is not raw memory.

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

Arrays are not thread-safe. Arrays are value types, which means that they support copy-on-write in a thread-safe way (so you can freely pass an array to another thread, and if it is copied there, that is ok), but you can't mutate the same array on multiple threads. An Array is not a C buffer. It's not promised to have contiguous memory. It's not even promised to allocate memory at all. Array could choose internally to store "I'm currently all zeros" as a special state and just return 0 for every subscript. (It doesn't, but it's allowed to.)

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

For this specific problem, you'd typically use vDSP methods like vDSP_vramp, but I understand this is just an example, and there may not be a vDSP method that solves the problem. Typically, though, I'd still focus on Accelerate/SIMD methods rather than dispatching to queues.

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

But if you are going to dispatch to queues, you'll need an UnsafeMutableBuffer to take control of the memory (and ensure that the memory even exists):

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)
        }
    }
}

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

The "Unsafe" indicates that it's now your problem to make sure all the accesses are legal and that you're not creating race conditions.

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

Note the use of .concurrentPerform here. As @user3441734 reminds us, pixelsPtr is not promised to be valid once .withUnsafeMutablePointer completes. .concurrentPerform is guaranteed not to return until all blocks are complete, so the pointer is guaranteed to be valid.

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

This could be done with DispatchGroup as well, but the .wait would need to be inside of withUnsafeMutableBufferPointer.

这篇关于同时填充数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆