DispatchQueue.concurrentPerform损坏的内存

时间:2018-11-17 00:13:31

标签: ios multithreading

我发现以下代码(在Playground上运行)有一个奇怪的行为。

import Foundation

let count = 100
var array = [[Int]](repeating:[Int](), count:count)

DispatchQueue.concurrentPerform(iterations: count) { (i) in
    array[i] = Array(i..<i+count)
}

// Evaluation
for (i,value) in array.enumerated() {
    if (value.count != count) {
        print(i, value.count)
    }
}

每次的结果都不同,有时会因内存损坏而崩溃。似乎在另一个线程访问内存时正在发生(“数组”的)内存重新分配。

这是(iOS的)错误还是预期的行为?我想念什么吗?

3 个答案:

答案 0 :(得分:2)

这是预期的行为。 Swift数组不是线程安全的;也就是说,同时修改多个线程的Swift数组会导致损坏。

我意识到您只是在做实验,但是即使数组是线程安全的,也不能很好地使用concurrentPerform,并且考虑到线程开销,它可能比简单的for循环性能差

一旦您引入了适当的同步方法来保护阵列更新,例如将更新分派到串行分派队列中,它肯定会比简单的for循环更慢执行< / p>

答案 1 :(得分:0)

这是解决方案。感谢您的快速回复。

import Foundation

let count = 1000
var arrays = [[Int]](repeating:[Int](), count:count)
let dispatchGroup = DispatchGroup()
let lockQueue = DispatchQueue(label: "lockQueue")

DispatchQueue.concurrentPerform(iterations: count) { (i) in
    dispatchGroup.enter()
    let array = Array(i..<i+count) // The actual code is very complex
    lockQueue.async {
        arrays[i] = array
        dispatchGroup.leave()
    }
}

dispatchGroup.wait()

// Evaluation
for (i,value) in arrays.enumerated() {
    if (value.count != count) {
        print(i, value.count)
    }
}

答案 2 :(得分:0)

在我的情况下,我不得不每秒Float多次生成24k元素数组,而在旧版iPhone 6上,每个数组花费大约40ms。

由于数组的每个元素只分配了一次,所以我决定尝试使用指针来处理原始数组:

class UnsafeArray<T> {
    let count: Int
    let cArray: UnsafeMutablePointer<T>
    
    init(_ size: Int) {
        count = size
        cArray = UnsafeMutablePointer<T>.allocate(capacity: size)
    }
    
    subscript(index: Int) -> T {
        get { return cArray[index] }
        set { cArray[index] = newValue }
    }
    
    deinit {
        free(cArray)
    }
}

然后我像这样使用它:

let result = UnsafeArray<Float>(24000)
DispatchQueue.concurrentPerform(iterations: result.count, execute: { i in
    result[i] = someCalculation()
})

它奏效了!现在需要9到16毫秒。另外,使用此代码也不会造成内存泄漏。