创建线程安全数组,这很简单吗?

时间:2018-01-19 19:35:02

标签: arrays swift thread-safety

我刚刚阅读了a post by Basem Emara关于创建一个线程安全数组类型的Swift。当我浏览代码示例时,我问自己是否有一种方法可以用更少的代码来实现这一点。

假设我创建了这个类:

// MARK: Class Declaration
class ThreadsafeArray<Element> {
    // Private Variables
    private var __array: [Element] = []
    private var __arrayQueue: DispatchQueue = DispatchQueue(
        label: "ThreadsafeArray.__concurrentArrayQueue",
        attributes: .concurrent
    )
}


// MARK: Interface
extension ThreadSafeArray {
    // ReadWrite Variables
    var threadsafe: [Element] {
        get {
            return self.__arrayQueue.sync {
                return self.__array
            }
        }
        set(newArray) {
            self.__arrayQueue.async(flags: .barrier) {
                self.__array = newArray
            }
        }
    }
}

从现在开始,我只通过.threadsafe访问了实际数组,这是否足以使数组线程安全?

另外,我可以将它实现为结构而不是类来获取变异检查吗?

我知道这个数组中的对象本身并不是线程安全的,但这不是重点,所以我们假设我只把线程安全的东西放在那里。

(当然,为了避免调用.threadsafe,我会让闪亮的新类符合ExpressibleByArrayLiteralCollectionRangeReplaceableCollection,因此我可以使用它像一个普通的数组。

修改

与此同时,我已经尝试过测试它in a playground并开始相信它不够。

游乐场代码:

import Foundation
import PlaygroundSupport


PlaygroundPage.current.needsIndefiniteExecution = true

// Testing //
// Thread-unsafe array
func unsafeArray() {
    var array: [Int] = []
    var iterations: Int = 1000
    let start: TimeInterval = Date().timeIntervalSince1970

    DispatchQueue.concurrentPerform(iterations: iterations) { index in
        let last: Int = array.last ?? 0
        array.append(last + 1)

        DispatchQueue.global().sync {
            iterations -= 1

            // Final loop
            guard iterations <= 0 else { return }
            print(String(
                format: "Unsafe loop took %.3f seconds, count: %d.",
                Date().timeIntervalSince1970 - start, array.count
            ))
        }
    }
}

// Thread-safe array
func safeArray() {
    let array: ThreadsafeArray<Int> = ThreadsafeArray<Int>()
    var iterations: Int = 1000
    let start: TimeInterval = Date().timeIntervalSince1970

    DispatchQueue.concurrentPerform(iterations: iterations) { index in
        let last: Int = array.threadsafe.last ?? 0
        array.threadsafe.append(last + 1)

        DispatchQueue.global().sync {
            iterations -= 1

            // Final loop
            guard iterations <= 0 else { return }
            print(String(
                format: "Safe loop took %.3f seconds, count: %d.",
                Date().timeIntervalSince1970 - start, array.threadsafe.count
            ))
        }
    }
}

unsafeArray()
safeArray()

输出:

大部分时间:

experiments(31117,0x7000038d0000) malloc: *** error for object 0x11f663d28: pointer being freed was not allocated

***在malloc_error_break中设置断点以进行调试

有时:

IndexError: Index out of range

不幸的是:

Unsafe loop took 1.916 seconds, count: 994.
Safe loop took 11.258 seconds, count: 515.

似乎不够(也是,它非常难以理解)。

2 个答案:

答案 0 :(得分:0)

我怀疑这是您的问题:

DispatchQueue.global().sync { ...

如果在此处指定要使用的一个串行队列,则应获得所需的结果。

类似的东西:

let array = SynchronizedArray<Int>()
var iterations = 1000
let queue = DispatchQueue(label: "queue")

DispatchQueue.concurrentPerform(iterations: 1000) { index in
    array.append(array.last ?? 0)

    queue.sync {
        iterations -= 1

        if iterations == 0 {
            print(array.count)
        }
    }
}

答案 1 :(得分:-1)

另一种锁定对象的方法是:

func lock(obj: AnyObject, work:() -> ()) {
    objc_sync_enter(obj)
    work()
    objc_sync_exit(obj)
}

您的班级可以在需要时使用它来锁定其标准数组吗?