tom*_*kid 6 macos grand-central-dispatch ios 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]
Run Code Online (Sandbox Code Playgroud)
但收到类似的东西
2:[3, 19]
Run Code Online (Sandbox Code Playgroud)
这是游乐场代码:
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()
Run Code Online (Sandbox Code Playgroud)
我在下面查了一下:
如果定义OrderdSet为类(不是结构),则可以.
如果使用信号量而不是使用串行队列,则可以.
我想知道结构和串行队列对不稳定的原因.
- - 更新
我得到了预期的结果.
class而不是struct
import Foundation
import XCPlayground
class OrderedSet<T: Equatable> {
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)") // It's OK
}
XCPSetExecutionShouldContinueIndefinitely()
Run Code Online (Sandbox Code Playgroud)信号量而不是串行队列
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()
Run Code Online (Sandbox Code Playgroud)具有OrderdSet本身的串行队列.
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()
Run Code Online (Sandbox Code Playgroud)此代码将捕获的当前值testSet:
dispatch_group_async(group, globalQueue) {
testSet.append(i) // `testSet` inside the closure will be a copy of the `testSet` variable outside
}
Run Code Online (Sandbox Code Playgroud)
执行完闭包后,内部的值testSet将被复制到外部testSet变量中。
想象一个并发世界:
10个闭包同时运行,捕获outside的初始值testSet,即“0:[]”。
完成后,testSet闭包内 s 的 10 个副本会尝试复制回唯一的外部testSet。然而,胜利者只有一个,比如,外部的当前值为testSet“1:[3]”。
又一轮开始,捕获外部的当前值testSet“1:[3]”,追加i并复制回来,产生奇怪的结果,例如“2:[3, 19]”
在更新的情况 1 中,更改OrderedSet为类,事情非常简单,testSet通过引用捕获,并且所有线程都共享同一个对象。
在更新的案例 3 中,通过使用串行队列,我猜每个附加和复制回操作都是串行的,因此您会产生一个完美的有序集。
情况2更为复杂。事实上我还没有弄清楚幕后发生了什么。我认为这更多的是关于 swift 编译器的实现细节,并且可能会随着不同的 swift 版本而改变。信号量似乎是一种引用类型,因此“testSet”的所有副本都共享相同的信号量。testSet我猜编译器决定在这种情况下进行一些优化,并使s'的所有副本都__element指向同一个数组。因此结果包含所有 0..<20 个元素,但顺序是不可预测的。