GCD之dispatch_async;dispatch_sync;dispatch_async_f;dispatch_sync_f

一、概念與類型

對(duì)于GCD來(lái)說(shuō),所有的執(zhí)行都放到隊(duì)列中(queue),隊(duì)列的特點(diǎn)是FIFO(先提交的先執(zhí)行)。
GCD的隊(duì)列分為幾種,主隊(duì)列(main),全局隊(duì)列(global),用戶創(chuàng)建隊(duì)列(create)
對(duì)于全局隊(duì)列,默認(rèn)有四個(gè),分為四個(gè)優(yōu)先級(jí)
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

DISPATCH_QUEUE_PRIORITY_HIGH :優(yōu)先級(jí)最高,在default,和low之前執(zhí)行
DISPATCH_QUEUE_PRIORITY_DEFAULT 默認(rèn)優(yōu)先級(jí),在low之前,在high之后
DISPATCH_QUEUE_PRIORITY_LOW 在high和default后執(zhí)行
DISPATCH_QUEUE_PRIORITY_BACKGROUND:提交到這個(gè)隊(duì)列的任務(wù)會(huì)在high優(yōu)先級(jí)的任務(wù)和已經(jīng)提交到background隊(duì)列的執(zhí)行完后執(zhí)行。官方文檔:(the queue is scheduled for execution after all high priority queues have been scheduled and the system runs items on a thread whose priority is set for background status.)

二、dispatch_async與dispatch_sync

  • dispatch_sync
    提交到隊(duì)列中同步執(zhí)行
  • dispatch_async
    提交到隊(duì)列中異步執(zhí)行,立即返回
  • dispatch_barrier_sync
  • dispatch_barrier_async
    在隊(duì)列中,柵欄塊必須單獨(dú)執(zhí)行,不能與其它塊并行。這只對(duì)隊(duì)列有意義,因?yàn)榇嘘?duì)列中的塊總是按順序逐個(gè)來(lái)執(zhí)行的。并發(fā)隊(duì)列中如果發(fā)現(xiàn)接下來(lái)要處理的塊是個(gè)柵欄塊(barrier block),那么就一直等到當(dāng)前所有并發(fā)塊都執(zhí)行完畢,才會(huì)單獨(dú)執(zhí)行這個(gè)柵欄塊。

這是Effective Objective-C 2.0 這本書 中對(duì)dispatch_barrier_async的說(shuō)明,書中有一段示例代碼,大概是這樣的:

 - (instancetype)init
 {
        self = [super init];
        if (!self) {
              return nil;
        }
        _concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
       return self;
  }

 - (NSString *)name
 {
         __block NSString *name;
        dispatch_sync(_concurrentQueue, ^{
             name = _name;
        });
        return name;
 }

 - (void)setName:(NSString *)name
 {
       dispatch_barrier_async(_concurrentQueue, ^{
             _name = name;
      });
  }

問(wèn)題來(lái)了,以上代碼能實(shí)現(xiàn)對(duì)name屬性的讀寫同步嗎?
按道理,是可以的。但事實(shí)并不可以,因?yàn)樵谶@里蘋果給我們挖了一個(gè)坑。。。
原因就是 在這里 我們使用的是全局并發(fā)隊(duì)列。。
都是 并發(fā)隊(duì)列 憑啥就不一樣呢??

就是說(shuō),柵欄塊應(yīng)該在自己創(chuàng)建的并行隊(duì)列里執(zhí)行,如果是在串行隊(duì)列或是全局并行隊(duì)列中執(zhí)行,那么就起不到柵欄的作用,和dispatch_async 函數(shù)效果一樣了。

所以上面代碼只要把_concurrentQueue 改成自己創(chuàng)建的

 _concurrentQueue = dispatch_queue_create("com.people.test", DISPATCH_QUEUE_CONCURRENT);

就可以實(shí)現(xiàn) 讀寫的同步了。

隔離隊(duì)列

再來(lái)理解一個(gè)隔離隊(duì)列的概念,也是跟dispatch_barrier_sync和dispatch_barrier_async有關(guān)。

例如操作系統(tǒng)中的經(jīng)典的讀者-寫者問(wèn)題,簡(jiǎn)而言之,我們可以在同一時(shí)刻有多個(gè)讀者,但同一時(shí)刻只能有一個(gè)線程可以寫入。解決方法就是利用GCD的四種 APIs

  • dispatch_sync
  • dispatch_async
  • dispatch_barrier_sync
  • dispatch_barrier_async

我們的想法是讀操作可以支持同步和異步,而寫操作也能支持異步寫入,并且必須確保是寫入的是同一個(gè)引用。GCD 的 barrier 集合 APIs 提供了解決方案:他們將會(huì)一直等到隊(duì)列中的任務(wù)清空,才會(huì)繼續(xù)執(zhí)行 block。使用 barrier APIs 可以用來(lái)限制我們對(duì)字典對(duì)象的寫入,并且確保我們不會(huì)在同一時(shí)刻進(jìn)行多個(gè)寫操作,以及正在寫操作時(shí)同時(shí)進(jìn)行讀操作??匆欢未a:

 class IdentityMap<T: Identifiable> {  
var dictionary = Dictionary<String, T>()
let accessQueue = dispatch_queue_create("com.khanlou.isolation.queue", DISPATCH_QUEUE_CONCURRENT)

func object(withID ID: String) -> T? {
    var result: T? = nil
    dispatch_sync(accessQueue, {
        result = dictionary[ID] as T?
    })
    return result
}

func addObject(object: T) {
    dispatch_barrier_async(accessQueue, {
        dictionary[object.ID] = object
    })
}
}

dispatch_sync 將會(huì)分發(fā) block 到我們的隔離隊(duì)列上然后等待其執(zhí)行完畢。通過(guò)這種方式,我們就實(shí)現(xiàn)了同步讀操作(如果我們搞成異步讀取,getter 方法就需要一個(gè) completion block)。因?yàn)?accessQueue 是并發(fā)隊(duì)列,這些同步讀取操作可以并發(fā)執(zhí)行,也就是允許同時(shí)讀。

dispatch_barrier_async 將分發(fā) block 到隔離隊(duì)列上,async 異步部分意味著會(huì)立即返回,并不會(huì)等待 block 執(zhí)行完畢。這對(duì)性能是有好處的,但是在一個(gè)寫操作后立即執(zhí)行一個(gè)讀操作會(huì)導(dǎo)致讀到一個(gè)半成品的數(shù)據(jù)(因?yàn)榭赡軐懖僮鬟€未完成就開始讀了)。

dispatch_barrier_async 中的 barrier 部分意味著:只要 barrier block 進(jìn)入隊(duì)列,并不會(huì)立即執(zhí)行,而是會(huì)等待該隊(duì)列其他 block 執(zhí)行完畢后再執(zhí)行。所以在這點(diǎn)上就保證了我們的 barrier block 每次都只有它自己在執(zhí)行。而所有在他之后提交的 block 也會(huì)一直等待這個(gè) barrier block 執(zhí)行完再執(zhí)行。

傳入 dispatch_barrier_async() 函數(shù)的 queue,必須是用 dispatch_queue_create 創(chuàng)建的并發(fā) queue。如果是串行 queue 或者是 global concurrent queues,這個(gè)函數(shù)就會(huì)變成 dispatch_async() 了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 簡(jiǎn)介 GCD(Grand Central Dispatch)是在macOS10.6提出來(lái)的,后來(lái)在iOS4.0被引...
    sunmumu1222閱讀 901評(píng)論 0 2
  • 本篇博客共分以下幾個(gè)模塊來(lái)介紹GCD的相關(guān)內(nèi)容: 多線程相關(guān)概念 多線程編程技術(shù)的優(yōu)缺點(diǎn)比較? GCD中的三種隊(duì)列...
    dullgrass閱讀 37,913評(píng)論 30 236
  • iOS中GCD的使用小結(jié) 作者dullgrass 2015.11.20 09:41*字?jǐn)?shù) 4996閱讀 20199...
    DanDanC閱讀 901評(píng)論 0 0
  • 我們知道在iOS開發(fā)中,一共有四種多線程技術(shù):pthread,NSThread,GCD,NSOperation: ...
    請(qǐng)叫我周小帥閱讀 1,514評(píng)論 0 1
  • 最近常常晚上一點(diǎn)左右被痛苦的咳嗽憋醒。剛開始特別難受,坐起來(lái)后做一些自己白天沒(méi)來(lái)得及做的不正經(jīng)的或者想做的事情,半...
    學(xué)會(huì)取悅自己閱讀 242評(píng)論 0 0