首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Swift 2:结构螺纹-安全

Swift 2:结构螺纹-安全
EN

Stack Overflow用户
提问于 2015-07-25 11:29:28
回答 3查看 1.1K关注 0票数 6

在我的快速实践中,我编写了名为OrderedSet的简单结构。

我尝试OrderedSet是一个线程安全与GCD串行队列。

但这不管用。试验结果不稳定。我期待着这样的事情:

代码语言:javascript
复制
20:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

但却收到了类似于

代码语言:javascript
复制
2:[3, 19]

这是操场代码:

代码语言:javascript
复制
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),则可以。

如果使用信号量而不是使用串行队列,这是可以的。

我想知道这对结构和串行队列不稳定的原因。

-更新

我用这些得到了预期的结果。

  1. 类而不是结构类。 导入基础导入XCPlayground类OrderedSet { func追加(e: T) { dispatch_sync(q) { if !self.__elements.contains(e) }} var元素:t{ var元素:t= [] dispatch_sync(q) {元素= self.__elements }返回元素} var计数: Int { var ret =0 dispatch_sync(q) { ret = self.__elements.count } ret }私有var __elements: t= []私有let q=self.__elements))扩展OrderedSet: CustomStringConvertible { var description: String { var text =“dispatch_sync(q) { text =”(self.__elements.count):(self.__elements)}返回text } //测试代码let globalQueue =dispatch_sync)设组= dispatch_group_create() var testSet = OrderedSet() in 0..<20 { dispatch_group_async( group,globalQueue) { testSet.append(i) }dispatch_group_notify(组,globalQueue) { print("(testSet)") // It} XCPSetExecutionShouldContinueIndefinitely()
  2. 信号量而不是串行队列 导入基础导入XCPlayground结构OrderedSet {变异函数追加(e: T) { dispatch_semaphore_wait(s,DISPATCH_TIME_FOREVER) if !self.__elements.contains(e) { self.__elements.append(e) } dispatch_semaphore_signal(s) } var元素:t{ var元素:t= [] dispatch_semaphore_wait(s,s)DISPATCH_TIME_FOREVER)元素= self.__elements dispatch_semaphore_signal(s)返回元素} var计数: Int { var =0 dispatch_semaphore_wait(s,DISPATCH_TIME_FOREVER) ret = self.__elements.count dispatch_semaphore_signal(s)返回ret }私有变量__elements: t= []私有,让s= dispatch_semaphore_create(1) }扩展OrderedSet: CustomStringConvertible { var description: String { var text = "“dispatch_semaphore_wait(s,( DISPATCH_TIME_FOREVER) DISPATCH_TIME_FOREVER= "(self.__elements.count):(self.__elements)“dispatch_semaphore_signal(s)返回text } //测试代码let globalQueue =globalQueue 0) let group = dispatch_group_create() var testSet = OrderedSet() in 0..<20 {dispatch_group_async(组,( globalQueue) { testSet.append(i) }} dispatch_group_notify(group,globalQueue) { print("(testSet)") // It's OK } XCPSetExecutionShouldContinueIndefinitely()
  3. 带有OrderdSet本身的串行队列。 导入基础导入XCPlayground结构OrderedSet {变异函数追加(e: T) { if !self.__elements.contains(e) } var元素:t{返回self.__elements } var计数: Int {返回self.__elements.count }私有var __elements: t= [])扩展OrderedSet: CustomStringConvertible { var description: String { return "(self.__elements.count):(self.__elements)“} //测试代码let globalQueue =self.__elements( 0)设serialQueue =dispatch_queue_create(“串行”,DISPATCH_QUEUE_SERIAL)让组= dispatch_group_create() var testSet = OrderedSet()表示0..<20 {dispatch_group_async(组,globalQueue) { dispatch_sync(serialQueue) { testSet.append(i) }dispatch_group_notify(组,serialQueue) { print("(testSet)") / It's } XCPSetExecutionShouldContinueIndefinitely()
EN

回答 3

Stack Overflow用户

发布于 2016-09-26 04:01:56

此代码将捕获当前值 of testSet

代码语言:javascript
复制
dispatch_group_async(group, globalQueue) {
    testSet.append(i) // `testSet` inside the closure will be a copy of the `testSet` variable outside 
}

在执行闭包之后,内部testSet的值将被复制到外部testSet变量。

想象一个并行的世界:

  • 10个闭包同时运行,捕获外部testSet的初始值,即"0:[]“。
  • 一旦完成,10份testSet的内部闭包试图复制回唯一的外部testSet。然而,只有一个赢家,比如说,外部testSet的当前值是"1:3“。
  • 又一轮开始,捕获外部testSet的当前值,即"1:3",追加i,然后复制回来,产生奇怪的结果,例如,"2:3,19“。

在更新的案例1 (将OrderedSet更改为类)中,事情非常直接,testSet是通过引用捕获的,并且所有线程都共享同一个对象。

在您更新的案例3中,通过使用串行队列,我想每个附加和复制回操作都是串行的,所以您会产生一个完美的有序集。

案例2更复杂。其实我还没弄清楚引擎盖下面到底是怎么回事。我认为它更多的是关于快速编译器的实现细节,并且可能会在不同的快速版本上发生变化。信号量似乎是一种引用类型,因此“测试集”的所有副本都共享相同的信号量。我猜编译器决定在这种情况下进行一些优化,并使testSet__element的所有副本指向同一个数组。因此,结果包含了所有的0..<20元素,但是顺序是不可预测的。

票数 3
EN

Stack Overflow用户

发布于 2017-01-17 12:21:27

我认为在结构中使用dispatch_sync时所发生的情况是,self被闭包隐式捕获为inout参数。

这意味着修改了一个副本,然后在返回时替换外部self。因此,会有多个并发的self副本被变异,然后破坏原始副本。

在信号量的情况下,没有关闭,所以没有捕获,所以自我就是自我。变异发生在原始的外部自我上,信号量确保每个人都有秩序地这样做。

在对结构中的getter和setter使用p线程互斥对象的闭包包装时,我遇到了同样的情况。即使闭包参数是不可转义的,结构(即self)似乎仍然被视为一个inout,因此会发生混乱的事情。

票数 0
EN

Stack Overflow用户

发布于 2019-12-04 05:56:39

iOS中的值类型存储在堆栈中,每个线程都有自己的堆栈。因此,在结构中,当您从不同的堆栈访问时,值将被复制。谢谢。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31626004

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档