美文网首页iOS DeveloperiOS开发
并行队列 线程安全

并行队列 线程安全

作者: upworld | 来源:发表于2016-12-27 09:46 被阅读280次

我的wiki地址
最近研读了SDWebImage源码。细细品味了下SDWebImageDownloaderOperation类如何利用并行queue(DISPATCH_QUEUE_CONCURRENT)实现多线程安全,兼并发

_barrierQueue=dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue",DISPATCH_QUEUE_CONCURRENT);

我们一般都利用串行queue(DISPATCH_QUEUE_SERIAL)的FIFO特性来实现多线程安全比较典型的开源库如FMDB就是利用串行queue来实现多线程安全,在SDWebImage中同样也有这样的案例如SDImageCache中的ioQueue就是串行queue。

_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);

串行queue的FIFO的特性比较容易理解,而且一次只有一个线程执行,所以比较好上手,在项目实践中屡试不爽。可是利用并行queue,我们一样能实现多线程安全,并且适当条件下可以利用并行queue达到多线程并发。废话少说,不信看看SDWebImageDownloaderOperation类中的源码。在贴出代码之前首先简单说明下SDWebImageDownloaderOperation类利用并行queue(barrierQueue)来控制所有对可变数组callbackBlocks添加,删除,读取的操作是多线程安全的。在后面代码中我们可以看到所有对callbackBlocks的操作都是在并行queue(barrierQueue)中的block中执行,来确保对callbackBlocks可变数组的操作是线程安全的

1,创建并行queue

_callbackBlocks = [NSMutableArray new];
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT);

2,调用dispatch_barrier_async

dispatch_barrier_async(self.barrierQueue, ^{
[self.callbackBlocks addObject:callbacks];
});

dispatch_barrier_async确保在执行[self.callbackBlocks addObject:callbacks]的时候一次只有一个线程执行,并且当执行的时候,并行queue(barrierQueue)不能执行其他block。至于为什么请参考dispatch_barrier系列API说明。后面你可以看到所有callbackBlocks的添加,删除,读取都是在barrierQueue中读取。

tips:Dispatch Barrier API说明
/*!

  • @functiongroup Dispatch Barrier API
  • The dispatch barrier API is a mechanism for submitting barrier blocks to a
  • dispatch queue, analogous to the dispatch_async()/dispatch_sync() API.
  • It enables the implementation of efficient reader/writer schemes.
  • Barrier blocks only behave specially when submitted to queues created with
  • the DISPATCH_QUEUE_CONCURRENT attribute; on such a queue, a barrier block
  • will not run until all blocks submitted to the queue earlier have completed,
  • and any blocks submitted to the queue after a barrier block will not run
  • until the barrier block has completed.
  • When submitted to a a global queue or to a queue not created with the
  • DISPATCH_QUEUE_CONCURRENT attribute, barrier blocks behave identically to
  • blocks submitted with the dispatch_async()/dispatch_sync() API.
    */

3,调用dispatch_sync

- (nullable NSArray *)callbacksForKey:(NSString *)key {
__block NSMutableArray *callbacks = nil;
dispatch_sync(self.barrierQueue, ^{
// We need to remove [NSNull null] because there might not always be a progress block for each callback
callbacks = [[self.callbackBlocks valueForKey:key] mutableCopy];
[callbacks removeObjectIdenticalTo:[NSNull null]];
});
return [callbacks copy];// strip mutability here
}

这里是关键,并行queue的并发特性在SDWebImageDownloaderOperation类中就体现在dispatch_sync的调用里。在这里注意了dispatch_sync在并发queue并不是线程安全的。因为并行queue中,很有可能有多个线程同时并行执行dispatch_sync中的block。在这里需要我们理解dispatch_sync在并发queue中如何实现并发的,例如有5个线程同时执行dispatch_sync(self.barrierQueue,block),那就有可能有5个线程同时执行block,这样block就并发了(如果不是很了解dispatch_sync在并行queue如何并发可以到TestGCDQueue执行测试方法testMethod_8,就明白了)。所以在这里我们只读取self.callbackBlocks中某个key值对应的value(多线程里面的读取self.callbackBlock是线程安全的),然后mutableCopy一个实例callbacks。然后在callbacks中删除一个元素,再返回。

4,调用dispatch_barrier_sync

__block BOOL shouldCancel = NO;
dispatch_barrier_sync(self.barrierQueue, ^{
[self.callbackBlocks removeObjectIdenticalTo:token];
if (self.callbackBlocks.count == 0) {
shouldCancel = YES;
}
});

解释和2一致,唯一区别就是dispatch_barrier_sync是同步的dispatch_barrier_async是异步的。

5,调用dispatch_barrier_async

- (void)reset {
dispatch_barrier_async(self.barrierQueue, ^{
[self.callbackBlocks removeAllObjects];
});
...
}

解释和2一致
SDWebImageDownloaderOperation类中所有和barrierQueue相关的函数就这些了。看完上面解释是否明白了

  • 为何用barrierQueue是并行queue而不是串行queue?
  • 你是否明白了为什么self.callbackBlocks的添加和删除都是在dispatch_barrier系列函数执行?
  • 在并行queue中dispatch_sync多个线程可以同时执行?
  • 为什么要读取self.callbackBlocks中key对应的值,然后mutablecopy才删除元素。

想要了解更详细代码可以下载源码SDWebImage定位到SDWebImageDownloaderOperation中,慢慢回味吧。

如果对dispatch_barrier_async,dispatch_barrier_sync,dispatch_async,dispatch_sync,并行queue,串行queue等不是很了解。可以到我的github上下载测试工程TestGCDQueue运行下。

相关文章

  • 并行队列 线程安全

    我的wiki地址最近研读了SDWebImage源码。细细品味了下SDWebImageDownloaderOpera...

  • GCD

    1、同步串行队列 2、同步并行队列 3、异步串行队列 4、异步并行队列 5、死锁 主线程中创建同步串行队列 主线程...

  • 常用的GCD记录一下

    子线程并行 串行 主线程 串行队列 子线程 并行队列 子线程 栅栏函数 控制执行顺序 避免数据竞争 多线...

  • 【iOS出租屋进阶】之多线程GCD详解

    线程、任务和队列的概念 异步、同步 & 并行、串行的特点 组合 |并行队列|串行队列 |主队列----|----|...

  • 队列,异步,同步,线程通俗理解

    一、队列 串行队列 并行队列 主队列(只在主线程执行的串行队列) 全局队列(系统的并行队列) 二、 任务(是否具有...

  • Swift队列和线程的搭配执行

    队列分为 串行队列,并行队列,特殊的主队列线程分为 同步线程,异步线程 搭配结果一共6种情况1:主队列,同步线程。...

  • iOS 多线程技术总结

    概览 进程与线程的概念 多线程的由来 并行与并发 多线程的实现 串行与并行 线程的几种状态 串行队列与并发队列区别...

  • iOS - Multi-Thread

    概念篇 进程 线程 多线程 单核多线程 & 多核多线程 并行 & 并发 同步 & 异步 队列 队列 & 任务 的执...

  • iOS开发笔记-多线程的使用方法

    多线程方式一:GCD队列的3个种类: 自建队列: 分并行/串行 全局队列: 属于并行队列, 是系统默认创建的. 主...

  • 多线程开发中的一些基本概念的理解

    进程、线程、队列(串行队列、并行队列)、同步(dispatch_sync)、异步(dispatch_async)、...

网友评论

    本文标题:并行队列 线程安全

    本文链接:https://www.haomeiwen.com/subject/favsvttx.html