- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
在我的快速实践中,我编写了名为 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]
这里是 playground 代码:
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和serial queue这对不稳定的原因。
----更新
我得到了预期的结果。
类而不是结构
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()
信号量代替串行队列
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()
本身带有 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()
最佳答案
此代码将捕获 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
副本尝试复制回唯一的外部 testSet
。但是,只有一个赢家,比如说,外部 testSet
的当前值为“1:[3]”。
又一轮开始,捕获外部 testSet
的当前值“1:[3]”,附加 i
,然后复制回来,产生奇怪的结果,比如说,“2:[3, 19]”
在您更新的案例 1 中,将 OrderedSet
更改为类,事情非常简单,testSet
通过引用捕获,并且所有线程都共享同一个对象。
在您更新的案例 3 中,通过使用串行队列,我猜每个追加和复制回操作都是串行的,因此您会产生一个完美的有序集。
情况 2 更为复杂。其实我还没有弄清楚引擎盖下发生了什么。而且我认为这更多是关于 swift 编译器的实现细节,并且可能会随着不同的 swift 版本而改变。似乎信号量是一种引用类型,因此“testSet”的所有副本都共享相同的信号量。我想编译器决定在这种情况下进行一些优化,并使 testSet
的所有副本 __element
指向同一个数组。因此结果包含所有 0..<20 个元素,但顺序不可预测。
关于ios - swift 2 : struct thread-safety,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31626004/
IO 设备如何知道属于它的内存中的值在memory mapped IO 中发生了变化? ? 例如,假设内存地址 0 专用于保存 VGA 设备的背景颜色。当我们更改 memory[0] 中的值时,VGA
我目前正在开发一个使用Facebook sdk登录(通过FBLoginView)的iOS应用。 一切正常,除了那些拥有较旧版本的facebook的人。 当他们按下“使用Facebook登录”按钮时,他
假设我有: this - is an - example - with some - dashesNSRange将使用`rangeOfString:@“-”拾取“-”的第一个实例,但是如果我只想要最后
Card.io SDK提供以下详细信息: 卡号,有效期,月份,年份,CVV和邮政编码。 如何从此SDK获取国家名称。 - (void)userDidProvideCreditCardInfo:(Car
iOS 应用程序如何从网络服务下载图片并在安装过程中将它们安装到用户的 iOS 设备上?可能吗? 最佳答案 您无法控制应用在用户设备上的安装,因此无法在安装过程中下载其他数据。 只需在安装后首次启动应
我曾经开发过一款企业版 iOS 产品,我们公司曾将其出售给大型企业,供他们的员工使用。 该应用程序通过 AppStore 提供,企业用户获得了公司特定的配置文件(包含应用程序配置文件)以启用他们有权使
我正在尝试将 Card.io SDK 集成到我的 iOS 应用程序中。我想为 CardIO ui 做一个简单的本地化,如更改取消按钮标题或“在此保留信用卡”提示文本。 我在 github 上找到了这个
我正在使用 CardIOView 和 CardIOViewDelegate 类,没有可以设置为 YES 的 BOOL 来扫描 collectCardholderName。我可以看到它在 CardIOP
我有一个集成了通话工具包的 voip 应用程序。每次我从我的 voip 应用程序调用时,都会在 native 电话应用程序中创建一个新的最近通话记录。我在 voip 应用程序中也有自定义联系人(电话应
iOS 应用程序如何知道应用程序打开时屏幕上是否已经有键盘?应用程序运行后,它可以接收键盘显示/隐藏通知。但是,如果应用程序在分屏模式下作为辅助应用程序打开,而主应用程序已经显示键盘,则辅助应用程序不
我在模拟器中收到以下错误: ImageIO: CGImageReadSessionGetCachedImageBlockData *** CGImageReadSessionGetCachedIm
如 Apple 文档所示,可以通过 EAAccessory Framework 与经过认证的配件(由 Apple 认证)进行通信。但是我有点困惑,因为一些帖子告诉我它也可以通过 CoreBluetoo
尽管现在的调试器已经很不错了,但有时找出应用程序中正在发生的事情的最好方法仍然是古老的 NSLog。当您连接到计算机时,这样做很容易; Xcode 会帮助弹出日志查看器面板,然后就可以了。当您不在办公
在我的 iOS 应用程序中,我定义了一些兴趣点。其中一些有一个 Kontakt.io 信标的名称,它绑定(bind)到一个特定的 PoI(我的意思是通常贴在信标标签上的名称)。现在我想在附近发现信标,
我正在为警报提示创建一个 trigger.io 插件。尝试从警报提示返回数据。这是我的代码: // Prompt + (void)show_prompt:(ForgeTask*)task{
您好,我是 Apple iOS 的新手。我阅读并搜索了很多关于推送通知的文章,但我没有发现任何关于 APNS 从 io4 到 ios 6 的新更新的信息。任何人都可以向我提供 APNS 如何在 ios
UITabBar 的高度似乎在 iOS 7 和 8/9/10/11 之间发生了变化。我发布这个问题是为了让其他人轻松找到答案。 那么:在 iPhone 和 iPad 上的 iOS 8/9/10/11
我想我可以针对不同的 iOS 版本使用不同的 Storyboard。 由于 UI 的差异,我将创建下一个 Storyboard: Main_iPhone.storyboard Main_iPad.st
我正在写一些东西,我将使用设备的 iTunes 库中的一部分音轨来覆盖 2 个视频的组合,例如: AVMutableComposition* mixComposition = [[AVMutableC
我创建了一个简单的 iOS 程序,可以顺利编译并在 iPad 模拟器上运行良好。当我告诉 XCode 4 使用我连接的 iPad 设备时,无法编译相同的程序。问题似乎是当我尝试使用附加的 iPad 时
我是一名优秀的程序员,十分优秀!