作者:临冬将至 | 来源:互联网 | 2023-05-24 16:51
我有快速的线程问题.我有一个包含一些对象的数组.在委托上,类每秒都会获得新对象.之后我必须检查对象是否已经在数组中,所以我必须更新对象,否则我必须删除/添加新对象.
如果我添加一个新对象,我必须首先通过网络获取一些数据.这是handelt经过一个街区.
现在我的问题是,如何同步这项任务?
我尝试了一个dispatch_semaphore,但是这个阻止了UI,直到块完成.
我还尝试了一个简单的bool变量,它检查块当前是否执行并同时跳过compare方法.
但这两种方法都不理想.
什么是管理阵列的最佳方式,我不想在阵列中有重复的数据.
1> skim..:
Kirsteins是正确的,但您并不总是需要使用调度队列.您可以使用:
objc_sync_enter(array)
// manipulate the array
objc_sync_exit(array)
这应该做的伎俩.为了增加额外的好处,您可以创建一个在需要线程安全时使用的函数:
func sync(lock: NSObject, closure: () -> Void) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
...
var list = NSMutableArray()
sync (list) {
list.addObject("something")
}
请注意,我已更改AnyObject
为NSObject
.在Swift集合类型中实现为struct
s并且它们按值传递,因此我猜测使用方便函数时使用通过引用传递的可变集合类会更安全.sync
更新Swift
线程安全访问的推荐模式是使用dispatch barrier
:
let queue = DispatchQueue(label: "thread-safe-obj", attributes: .concurrent)
// write
queue.async(flags: .barrier) {
// perform writes on data
}
// read
var value: ValueType!
queue.sync {
// perform read and assign value
}
return value
在诸如`Array`之类的值类型上使用`objc_sync_enter`是不安全/正确的.`objc_sync_array`执行指针比较,并且不保证桥接Swift值类型的指针的生命周期是稳定的(实际上大多数时候它是*不*稳定).
你让我很好奇,所以我在'objc_sync_enter`上找到了这个:http://rykap.com/objective-c/2015/05/09/synchronized/
这不起作用,原因/sf/ask/17360801/
2> Kirsteins..:
我解决这个问题的方法是使用串行调度队列来同步对盒装数组的访问.当你试图获取索引值并且队列真的很忙时它会阻塞线程,但这也是锁的问题.
public class SynchronizedArray {
private var array: [T] = []
private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_SERIAL)
public func append(newElement: T) {
dispatch_async(self.accessQueue) {
self.array.append(newElement)
}
}
public subscript(index: Int) -> T {
set {
dispatch_async(self.accessQueue) {
self.array[index] = newValue
}
}
get {
var element: T!
dispatch_sync(self.accessQueue) {
element = self.array[index]
}
return element
}
}
}
var a = SynchronizedArray()
a.append(1)
a.append(2)
a.append(3)
// can be empty as this is non-thread safe access
println(a.array)
// thread-safe synchonized access
println(a[0])
println(a[1])
println(a[2])
您可以考虑使用读写器模式:使用`DISPATCH_QUEUE_CONCURRENT`; 将写入从`dispatch_async`更改为`dispatch_barrier_async`; 但是将读取保留为`dispatch_sync`.这为您提供了并发读取,但写入仍然是同步的.
你只对`dispatch_barrier_async`进行写操作.但是用`dispatch_sync`读取.因此,虽然写入不会(并且不应该)与其他任何内容同时发生,但是读取_can_与其他读取同时发生.请参阅WWDC 2012视频后半部分中的模式#6 [带块,GCD和XPC的异步设计模式](https://developer.apple.com/videos/play/wwdc2012-712/).
3> rmooney..:
Kirsteins的答案是正确的,但为方便起见,我已经用Amol Chaudhari和Rob的建议更新了答案,建议使用带有异步屏障的并发队列来允许并发读取但阻止写入.
我还包装了一些对我有用的其他数组函数.
public class SynchronizedArray {
private var array: [T] = []
private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_CONCURRENT)
public func append(newElement: T) {
dispatch_barrier_async(self.accessQueue) {
self.array.append(newElement)
}
}
public func removeAtIndex(index: Int) {
dispatch_barrier_async(self.accessQueue) {
self.array.removeAtIndex(index)
}
}
public var count: Int {
var count = 0
dispatch_sync(self.accessQueue) {
count = self.array.count
}
return count
}
public func first() -> T? {
var element: T?
dispatch_sync(self.accessQueue) {
if !self.array.isEmpty {
element = self.array[0]
}
}
return element
}
public subscript(index: Int) -> T {
set {
dispatch_barrier_async(self.accessQueue) {
self.array[index] = newValue
}
}
get {
var element: T!
dispatch_sync(self.accessQueue) {
element = self.array[index]
}
return element
}
}
}
更新
这是为Swift3更新的相同代码.
public class SynchronizedArray {
private var array: [T] = []
private let accessQueue = DispatchQueue(label: "SynchronizedArrayAccess", attributes: .concurrent)
public func append(newElement: T) {
self.accessQueue.async(flags:.barrier) {
self.array.append(newElement)
}
}
public func removeAtIndex(index: Int) {
self.accessQueue.async(flags:.barrier) {
self.array.remove(at: index)
}
}
public var count: Int {
var count = 0
self.accessQueue.sync {
count = self.array.count
}
return count
}
public func first() -> T? {
var element: T?
self.accessQueue.sync {
if !self.array.isEmpty {
element = self.array[0]
}
}
return element
}
public subscript(index: Int) -> T {
set {
self.accessQueue.async(flags:.barrier) {
self.array[index] = newValue
}
}
get {
var element: T!
self.accessQueue.sync {
element = self.array[index]
}
return element
}
}
}
removeAtIndex可以删除此代码中的错误项目导致idx冷却被其他线程'removeAtIndex'更改...
@mooney我认为@Speakus就在这里:说sychronized数组有一个元素,这不会失败吗?`if synchronizedArray.count == 1 {synchronizedArray.remove(at:0)}`这是一个竞争条件,比如两个线程执行该语句.两者同时读取1的计数,两者同时将写入块排队.写块顺序执行,第二个将失败.我错了吗?这是设计者还是用户错误?在任何一种情况下它都很脆弱,让我永远不想触摸多线程...你怎么能让这个安全?回到阅读和写作的独家锁?
4> nbloqs..:
一个小细节:在Swift 3中(至少在XCode 8 Beta 6中),队列的语法发生了显着变化.@Kirsteins答案的重要变化是:
private let accessQueue = DispatchQueue(label: "SynchronizedArrayAccess")
txAccessQueue.async() {
// Your async code goes here...
}
txAccessQueue.sync() {
// Your sync code goes here...
}