在我的快速实践中,我编写了名为OrderedSet的简单结构。
我尝试OrderedSet是一个线程安全与GCD串行队列。
但这不管用。试验结果不稳定。我期待着这样的事情:
20:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]但却收到了类似于
2:[3, 19]这是操场代码:
import Foundation
import XCPlayground
struct OrderedSet<T: Equatable> {
mutating func append(e: T) {
dispatch_sync(q) {
if !self.__elements.contains(e) {
self.__elements.append(e)
}
}
}
var elements: [T] {
var elements: [T] = []
dispatch_sync(q) {
elements = self.__elements
}
return elements
}
var count: Int {
var ret = 0
dispatch_sync(q) {
ret = self.__elements.count
}
return ret
}
private var __elements: [T] = []
private let q = dispatch_queue_create("OrderedSet.private.serial.queue", DISPATCH_QUEUE_SERIAL)
}
extension OrderedSet: CustomStringConvertible {
var description: String {
var text = ""
dispatch_sync(q) {
text = "\(self.__elements.count):\(self.__elements)"
}
return text
}
}
// Test code
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let group = dispatch_group_create()
var testSet = OrderedSet<Int>()
for i in 0..<20 {
dispatch_group_async(group, globalQueue) {
testSet.append(i)
}
}
dispatch_group_notify(group, globalQueue) {
print("\(testSet)") // unstable result
}
XCPSetExecutionShouldContinueIndefinitely()我查过如下:
如果将OrderdSet定义为类(而不是struct),则可以。
如果使用信号量而不是使用串行队列,这是可以的。
我想知道这对结构和串行队列不稳定的原因。
-更新
我用这些得到了预期的结果。
发布于 2016-09-26 04:01:56
此代码将捕获当前值 of testSet
dispatch_group_async(group, globalQueue) {
testSet.append(i) // `testSet` inside the closure will be a copy of the `testSet` variable outside
}在执行闭包之后,内部testSet的值将被复制到外部testSet变量。
想象一个并行的世界:
testSet的初始值,即"0:[]“。testSet的内部闭包试图复制回唯一的外部testSet。然而,只有一个赢家,比如说,外部testSet的当前值是"1:3“。testSet的当前值,即"1:3",追加i,然后复制回来,产生奇怪的结果,例如,"2:3,19“。在更新的案例1 (将OrderedSet更改为类)中,事情非常直接,testSet是通过引用捕获的,并且所有线程都共享同一个对象。
在您更新的案例3中,通过使用串行队列,我想每个附加和复制回操作都是串行的,所以您会产生一个完美的有序集。
案例2更复杂。其实我还没弄清楚引擎盖下面到底是怎么回事。我认为它更多的是关于快速编译器的实现细节,并且可能会在不同的快速版本上发生变化。信号量似乎是一种引用类型,因此“测试集”的所有副本都共享相同的信号量。我猜编译器决定在这种情况下进行一些优化,并使testSet‘__element的所有副本指向同一个数组。因此,结果包含了所有的0..<20元素,但是顺序是不可预测的。
发布于 2017-01-17 12:21:27
我认为在结构中使用dispatch_sync时所发生的情况是,self被闭包隐式捕获为inout参数。
这意味着修改了一个副本,然后在返回时替换外部self。因此,会有多个并发的self副本被变异,然后破坏原始副本。
在信号量的情况下,没有关闭,所以没有捕获,所以自我就是自我。变异发生在原始的外部自我上,信号量确保每个人都有秩序地这样做。
在对结构中的getter和setter使用p线程互斥对象的闭包包装时,我遇到了同样的情况。即使闭包参数是不可转义的,结构(即self)似乎仍然被视为一个inout,因此会发生混乱的事情。
发布于 2019-12-04 05:56:39
iOS中的值类型存储在堆栈中,每个线程都有自己的堆栈。因此,在结构中,当您从不同的堆栈访问时,值将被复制。谢谢。
https://stackoverflow.com/questions/31626004
复制相似问题