Ios Swift 2:结构线程安全
在我的swift实践中,我编写了名为Ios Swift 2:结构线程安全,ios,macos,swift,grand-central-dispatch,Ios,Macos,Swift,Grand Central Dispatch,在我的swift实践中,我编写了名为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 Ordered
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()
import Foundation
import XCPlayground
struct OrderedSet<T: Equatable> {
mutating func append(e: T) {
dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER)
if !self.__elements.contains(e) {
self.__elements.append(e)
}
dispatch_semaphore_signal(s)
}
var elements: [T] {
var elements: [T] = []
dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER)
elements = self.__elements
dispatch_semaphore_signal(s)
return elements
}
var count: Int {
var ret = 0
dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER)
ret = self.__elements.count
dispatch_semaphore_signal(s)
return ret
}
private var __elements: [T] = []
private let s = dispatch_semaphore_create(1)
}
extension OrderedSet: CustomStringConvertible {
var description: String {
var text = ""
dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER)
text = "\(self.__elements.count):\(self.__elements)"
dispatch_semaphore_signal(s)
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)") // It's OK
}
XCPSetExecutionShouldContinueIndefinitely()
<代码>导入基础
导入运动场
结构有序集{
变异函数追加(e:T){
调度信号灯等待(s,调度时间永远)
if!self.\u元素包含(e){
self.\u元素。追加(e)
}
调度信号灯信号
}
变量元素:[T]{
变量元素:[T]=[]
调度信号灯等待(s,调度时间永远)
元素=自身元素
调度信号灯信号
返回元素
}
变量计数:Int{
var-ret=0
调度信号灯等待(s,调度时间永远)
ret=自身元素数
调度信号灯信号
回程网
}
私有变量元素:[T]=[]
private let s=dispatch\u信号量\u create(1)
}
扩展OrderedSet:CustomStringConvertible{
变量说明:字符串{
var text=“”
调度信号灯等待(s,调度时间永远)
text=“\(self.\uu元素.计数):\(self.\uu元素)”
调度信号灯信号
返回文本
}
}
//测试代码
让globalQueue=dispatch\u get\u global\u queue(dispatch\u queue\u PRIORITY\u默认值,0)
let group=dispatch\u group\u create()
var testSet=OrderedSet()
对于0..中的i,此代码将捕获
testSet
的当前值:
dispatch_group_async(group, globalQueue) {
testSet.append(i) // `testSet` inside the closure will be a copy of the `testSet` variable outside
}
执行闭包后,内部testSet
的值将复制到外部testSet
变量
想象一个并行的世界:
- 10个闭包同时运行,捕获外部
的初始值,即“0:[]testSet
- 一旦完成,10个
s内部闭包的副本将尝试复制回唯一的外部testSet
。然而,只有一个胜利者,比如说,外部testSet
的当前值是“1:[3]”testSet
- 又一轮启动,捕获外部
测试集的当前值,即“1:[3]”,追加
,然后复制回来,产生奇怪的结果,比如“2:[3,19]”i
OrderedSet
更改为class,事情非常简单,testSet
通过引用捕获,所有线程共享同一个对象
在您更新的案例3中,通过使用串行队列,我猜每个追加和复制回操作都是串行的,因此您可以生成一个完美的有序集
案例2更为复杂。事实上我还没弄清楚引擎盖下到底发生了什么。我认为更多的是关于swift编译器的实现细节,可能会在不同的swift版本中发生变化。似乎信号量是一种引用类型,因此“testSet”的所有副本都共享相同的信号量。我猜编译器决定在这种情况下进行一些优化,并使
测试集
s'\u元素
的所有副本指向同一个数组。因此,结果包含所有0..我认为在结构内部使用dispatch\u sync
时发生的情况是,self
被闭包作为inout
参数隐式捕获
这意味着一个副本被修改,然后在返回时替换外部的self
。所以有多个同时复制的自我变异,然后重击原始
在信号量的情况下,没有闭包,所以没有捕获,所以self就是self就是self。变异发生在原始的外部自我上,信号量确保每个人都以有序的方式进行
在结构中使用pthread互斥体的闭包包装器作为getter和setter时,我遇到了同样的问题。即使闭包参数是非转义的,结构(即
self
)似乎仍然被视为inout
,因此会发生混乱的事情 iOS中的值类型存储在堆栈中,每个线程都有自己的堆栈。因此,在结构中,当您从不同的堆栈访问时,将复制值。谢谢。那么,症状是什么?我的预期结果是“20:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]”,但结果是“2:[3,19]”。@tom.e.kid请将测试代码分享如下:well@Kametrixom添加了一些代码,我得到了预期的结果。thanks@tom.e.kid关于您的预期结果,您不必期望它们是有序的(因为您是从该全局队列添加它们),但我同意我希望看到有序集中的所有元素。好问题。
import Foundation
import XCPlayground
struct OrderedSet<T: Equatable> {
mutating func append(e: T) {
if !self.__elements.contains(e) {
self.__elements.append(e)
}
}
var elements: [T] {
return self.__elements
}
var count: Int {
return self.__elements.count
}
private var __elements: [T] = []
}
extension OrderedSet: CustomStringConvertible {
var description: String {
return "\(self.__elements.count):\(self.__elements)"
}
}
// Test code
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL)
let group = dispatch_group_create()
var testSet = OrderedSet<Int>()
for i in 0..<20 {
dispatch_group_async(group, globalQueue) {
dispatch_sync(serialQueue) {
testSet.append(i)
}
}
}
dispatch_group_notify(group, serialQueue) {
print("\(testSet)") // It's OK
}
XCPSetExecutionShouldContinueIndefinitely()
dispatch_group_async(group, globalQueue) {
testSet.append(i) // `testSet` inside the closure will be a copy of the `testSet` variable outside
}