Ios Swift 2:结构线程安全

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

在我的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 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个闭包同时运行,捕获外部
      testSet
      的初始值,即“0:[]

    • 一旦完成,10个
      testSet
      s内部闭包的副本将尝试复制回唯一的外部
      testSet
      。然而,只有一个胜利者,比如说,外部
      testSet
      的当前值是“1:[3]”

    • 又一轮启动,捕获外部
      测试集的当前值,即“1:[3]”,追加
      i
      ,然后复制回来,产生奇怪的结果,比如“2:[3,19]”

    在更新的案例1中,将
    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 
    }