我试图使线程安全数组,但它的工作不是我所期望的
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)
我预计数组应该有一些混乱,但safeArray应该有从0到9的数字。
我知道array有3个值,而safeArray有10个值,但为什么这些值不是从0到9?
谢谢大家!
4条答案
按热度按时间o2gm4chl1#
barrier
的工作方式是确保DispatchWorkItem
(append(:_)
的代码块)在执行其perform
(代码块内的代码)之前等待,直到所有其他DispatchWorkItem
完成。如果你仔细观察,你会发现在
last
调用中有一个(DispatchWorkItem
),因为你调用last
是你在DispatchQueue.concurrentPerform
中并发做的第一件事,你会有一堆DispatchWorkItem
在队列中等待。这意味着您的所有
append(_:)
调用都将等待,因为它们被标记为barrier
,并且您的last
调用都将首先执行,因此会得到很多零,直到last
的所有DispatchWorkItem
都完成,然后再压缩两个appends(_:)
barrier
在并发队列中的工作方式是,它实际上会等到所有挂起的DispatchWorkItem
都完成后才启动,并且在它完成之前,不会有任何其他东西与它并发启动。除非我弄错了,否则它可以暂时“禁用”队列的并发特性。我通常不太愿意像其他人建议的那样引入锁或信号量,除非您首先了解GCD的工作原理,否则它们可能会导致更多问题。
看起来你要解决两个问题,首先是一个
append(_:)
可以并发工作,然后根据数组的当前状态在并行操作中改变数组。尝试先分解你要解决的问题,这样别人就可以给予你更好的答案。e3bfsja22#
为什么不使用
DispatchSemaphore
?4xy9mtcn3#
请记住,虽然barrier使操作本身成为线程安全的,但两个操作之间的顺序是未定义的。因此,如果你想依赖于
self.array.last
的值,你必须在 * 你能够通过barrier之后 * 才能得到它的值。也就是说,你可以有一个如下的函数:然后
打印预期结果:
kx1ctssn4#
我已经创建了一个线程安全的NSMutableArray,它的工作方式与预期一致。