DispatchQueue 障碍问题

wm.*_*1us 2 grand-central-dispatch ios swift

试图制作线程安全数组,但它的工作原理与我预期的不一样

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

public class SafeArray<Element> {
    private var array = [Element]()
    private let queue = DispatchQueue(label: "queueBarrier", attributes: .concurrent)

    public func append(element: Element) {
        queue.async(flags: .barrier) {
            self.array.append(element)
        }
    }

    public var elements: [Element] {
        var result = [Element]()
        queue.sync {
            result = self.array
        }
        return result
    }

    public var last: Element? {
        var result: Element?
        queue.sync {
            result = self.array.last
        }
        return result
    }
}




var safeArray = SafeArray<Int>()
var array = Array<Int>()

DispatchQueue.concurrentPerform(iterations: 10) { (int) in
    let last = array.last ?? 0
    array.append(last + 1)
    print("array = [..\(last)]")
}

print(array)

DispatchQueue.concurrentPerform(iterations: 10) { (int) in
    let last = safeArray.last ?? 0
    safeArray.append(element: last + 1)
       print("safeArray = [..\(last)]")
}

print(safeArray.elements)
Run Code Online (Sandbox Code Playgroud)

我的代码的输出

我预计数组应该有一些混乱,但 safeArray 应该有从 0 到 9 的数字。

我知道数组有 3 个值,但safeArray有预期的 10 个值。但是为什么这个值不是从 0 到 9?

谢谢!

Sei*_*van 6

工作的方法barrier是确保DispatchWorkItem( 的块append(:_))DispatchWorkItem在执行它的perform(块内的代码)之前等待所有其他s 完成。因此,一个barrier.

如果你仔细观察,你的电话中有一个 ( DispatchWorkItem) last。既然你打电话last,你做的第一件事同时DispatchQueue.concurrentPerform,你就会有一堆DispatchWorkItemS IN队列中等待。

这意味着您的所有append(_:)调用都将等待,因为它们被标记为,barrier并且您的last调用都将首先执行,从而获得很多零,直到所有DispatchWorkItems forlast都完成后,才将其压缩为两个appends(_:)

barrier在并发队列中的工作方式是,它实际上会等到所有挂起的DispatchWorkItems 完成后才开始,并且在它完成之前,没有其他任何东西会与它同时启动。除非我弄错了,否则它会暂时“禁用”队列的并发性质。

我通常不愿意按照其他人的建议引入锁或信号量,除非您首先了解 GCD 的工作原理,否则它们可能会导致更多问题。

看起来您正在尝试解决两件事,首先是让一个append(_:)并发工作,并在依赖于当前状态的并行操作中改变数组。尝试首先分解您要解决的问题,以便有人可以为您提供更好的答案。