在swift中创build线程安全数组
我有一个快速的线程问题。 我有一个数组中的一些对象。 在一个委托上,类每秒都会获得新的对象。 之后,我必须检查对象是否已经在数组中,所以我必须更新对象,否则我必须删除/添加新的对象。
如果我添加一个新的对象,我必须先通过networking获取一些数据。 这是handelt通过块。
现在我的问题是,我如何同步这个任务?
我已经尝试了一个dispatch_semaphore,但是这个阻塞了UI,直到块完成。
我也尝试了一个简单的boolvariables,它检查块是否正在执行,同时跳过比较方法。
但是这两种方法都不理想。
什么是pipe理arrays的最佳方式,我不希望在数组中有重复的数据。
Kirsteins是正确的,但你并不总是需要使用调度队列。 您可以使用:
objc_sync_enter(array) // manipulate the array objc_sync_exit(array)
这应该是诀窍。 为了获得额外的奖励,您可以创build一个函数,以便在需要线程安全时使用:
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中,集合types是以struct
的forms实现的,它们是按值传递的 ,所以我猜测在使用方便的sync
函数时,使用通过引用传递的 可变集合类会更安全。
我对这个问题的方法是使用串行调度队列,以同步访问盒装数组。 当你尝试获取索引处的值并且队列确实很忙的时候,它会阻塞线程,但这也是锁的问题。
public class SynchronizedArray<T> { 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<Int>() 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])
Kirsteins的答案是正确的,但为了方便起见,我已经更新了Amol Chaudhari和Rob关于使用具有asynchronous屏障的并发队列的build议,以允许并发读取但阻止写入。
我还包装了一些对我有用的数组函数。
public class SynchronizedArray<T> { 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<T> { 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 } } }
一个小细节:在Swift 3中(至less在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... }
我认为dispatch_barriers值得研究。 使用gcd进行同步对我来说比使用同步关键字更直观,以避免来自多个线程的状态变化。
https://mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html
这里有一个很好的答案是线程安全的,不会阻止并发读取: https : //stackoverflow.com/a/15936959/2050665
它写在目标C中,但移植到Swift是微不足道的。
@property (nonatomic, readwrite, strong) dispatch_queue_t thingQueue; @property (nonatomic, strong) NSObject *thing; - (id)init { ... _thingQueue = dispatch_queue_create("...", DISPATCH_QUEUE_CONCURRENT); ... } - (NSObject *)thing { __block NSObject *thing; dispatch_sync(self.thingQueue, ^{ thing = _thing; }); return thing; } - (void)setThing:(NSObject *)thing { dispatch_barrier_async(self.thingQueue, ^{ _thing = thing; }); }
使用DispatchQueue
进行同步
码:
class SomeClass { private let queue = DispatchQueue(label: "synchronize") //Not thread-safe private var _array : [Int]? //Thread-safe var array : [Int]? { get { return queue.sync { _array } } set { queue.sync { _array = newValue } } } }