对于一个NSString这样的属性,我们可以通过修改其原子性atomic,来保证其线程安全。但是对于NSMutableArray和NSMutableDictionary这样的对象,却无法通过这样的方案保障其线程安全。
通常为了保证可变容器的线程安全,我们会采用加读写锁或者采用GCD队列的方式保障其线程安全。加读写锁,可以保障线程安全,但是会失去多线程的优势。所以这里我们着重介绍一下通过GCD队列实现可变容器线程安全的方案。
首先介绍一下 dispatch_barrier_async
/*!
* @function dispatch_barrier_async
*
* @abstract
* Submits a barrier block for asynchronous execution on a dispatch queue.
*
* @discussion
* Submits a block to a dispatch queue like dispatch_async(), but marks that
* block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues).
*
* See dispatch_async() for details and "Dispatch Barrier API" for a description
* of the barrier semantics.
*
* @param queue
* The target dispatch queue to which the block is submitted.
* The system will hold a reference on the target queue until the block
* has finished.
* The result of passing NULL in this parameter is undefined.
*
* @param block
* The block to submit to the target dispatch queue. This function performs
* Block_copy() and Block_release() on behalf of callers.
* The result of passing NULL in this parameter is undefined.
*/
#ifdef __BLOCKS__
API_AVAILABLE(macos(10.7), ios(4.3))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
DISPATCH_REFINED_FOR_SWIFT
void
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
#endif
dispatch_barrier_async栅栏函数,可以将添加到队列中的所有任务分割开,在barrier之前添加的任务会在barrier之前执行,在barrier之后添加的任务会在barrier之后执行。因此,我们可以在执行写操作(如:addObject,removeObjectAtIndex等)时,通过dispatch_barrier_async添加任务,在执行读操作(如:objectAtIndex,indexOfObject等)时,通过dispatch_async添加任务。
但是,通过dispatch_async添加任务来执行读操作时,dispatch_async是另外开辟线程去执行的,不是立马返回的,所以我们不能立刻拿到执行结果,需要另写一个方法来返回读的结果,这和常规调用习惯不符。因此,这里我们不再采用dispatch_async,而是采用dispatch_sync。
dispatch_sync是在当前线程上执行,不会另外开辟新的线程,当线程返回的时候就可以拿到读取的结果。
下面拿NSMutableArray为例,讲一下具体实现:
这里要注意一点,dispatch_barrier_async仅对自定义并发队列DISPATCH_QUEUE_CONCURRENT有效。如果传递给此函数的队列是串行队列或全局并发队列之一,则此函数的行为类似于dispatch_sync函数。
- (instancetype)init {
if (self = [super init]) {
self.mArray = [[NSMutableArray alloc] initWithCapacity:0];
self.safeQueue = dispatch_queue_create("safe.mutableArray.queue", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (void)addObject:(id)object {
dispatch_barrier_async(self.safeQueue, ^{
[self.mArray addObject:object];
});
}
- (id)objectAtIndex:(NSUInteger)index; {
__block id result = nil;
dispatch_sync(self.safeQueue, ^{
result = [self.mArray objectAtIndex:index];
});
return result;
}