并行隊列 線程安全

我的wiki地址
最近研讀了SDWebImage源碼。細(xì)細(xì)品味了下SDWebImageDownloaderOperation類如何利用并行queue(DISPATCH_QUEUE_CONCURRENT)實(shí)現(xiàn)多線程安全,兼并發(fā)

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

我們一般都利用串行queue(DISPATCH_QUEUE_SERIAL)的FIFO特性來實(shí)現(xiàn)多線程安全比較典型的開源庫如FMDB就是利用串行queue來實(shí)現(xiàn)多線程安全,在SDWebImage中同樣也有這樣的案例如SDImageCache中的ioQueue就是串行queue。

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

串行queue的FIFO的特性比較容易理解,而且一次只有一個線程執(zhí)行,所以比較好上手,在項目實(shí)踐中屢試不爽??墒?strong>利用并行queue,我們一樣能實(shí)現(xiàn)多線程安全,并且適當(dāng)條件下可以利用并行queue達(dá)到多線程并發(fā)。廢話少說,不信看看SDWebImageDownloaderOperation類中的源碼。在貼出代碼之前首先簡單說明下SDWebImageDownloaderOperation類利用并行queue(barrierQueue)來控制所有對可變數(shù)組callbackBlocks添加,刪除,讀取的操作是多線程安全的。在后面代碼中我們可以看到所有對callbackBlocks的操作都是在并行queue(barrierQueue)中的block中執(zhí)行,來確保對callbackBlocks可變數(shù)組的操作是線程安全的

1,創(chuàng)建并行queue

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

2,調(diào)用dispatch_barrier_async

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

dispatch_barrier_async確保在執(zhí)行[self.callbackBlocks addObject:callbacks]的時候一次只有一個線程執(zhí)行,并且當(dāng)執(zhí)行的時候,并行queue(barrierQueue)不能執(zhí)行其他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,調(diào)用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
}

這里是關(guān)鍵,并行queue的并發(fā)特性在SDWebImageDownloaderOperation類中就體現(xiàn)在dispatch_sync的調(diào)用里。在這里注意了dispatch_sync在并發(fā)queue并不是線程安全的。因為并行queue中,很有可能有多個線程同時并行執(zhí)行dispatch_sync中的block。在這里需要我們理解dispatch_sync在并發(fā)queue中如何實(shí)現(xiàn)并發(fā)的,例如有5個線程同時執(zhí)行dispatch_sync(self.barrierQueue,block),那就有可能有5個線程同時執(zhí)行block,這樣block就并發(fā)了(如果不是很了解dispatch_sync在并行queue如何并發(fā)可以到TestGCDQueue執(zhí)行測試方法testMethod_8,就明白了)。所以在這里我們只讀取self.callbackBlocks中某個key值對應(yīng)的value(多線程里面的讀取self.callbackBlock是線程安全的),然后mutableCopy一個實(shí)例callbacks。然后在callbacks中刪除一個元素,再返回。

4,調(diào)用dispatch_barrier_sync

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

解釋和2一致,唯一區(qū)別就是dispatch_barrier_sync是同步的dispatch_barrier_async是異步的。

5,調(diào)用dispatch_barrier_async

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

解釋和2一致
SDWebImageDownloaderOperation類中所有和barrierQueue相關(guān)的函數(shù)就這些了??赐晟厦娼忉屖欠衩靼琢?/p>

  • 為何用barrierQueue是并行queue而不是串行queue?
  • 你是否明白了為什么self.callbackBlocks的添加和刪除都是在dispatch_barrier系列函數(shù)執(zhí)行?
  • 在并行queue中dispatch_sync多個線程可以同時執(zhí)行?
  • 為什么要讀取self.callbackBlocks中key對應(yīng)的值,然后mutablecopy才刪除元素。

想要了解更詳細(xì)代碼可以下載源碼SDWebImage定位到SDWebImageDownloaderOperation中,慢慢回味吧。

如果對dispatch_barrier_async,dispatch_barrier_sync,dispatch_async,dispatch_sync,并行queue,串行queue等不是很了解??梢缘轿业膅ithub上下載測試工程TestGCDQueue運(yùn)行下。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容