GCD的知識(shí)整理

參考:https://juejin.im/post/5a90de68f265da4e9b592b40

基礎(chǔ)API

// 系統(tǒng)標(biāo)準(zhǔn)提供的兩個(gè)隊(duì)列
// 全局隊(duì)列,也是一個(gè)并行隊(duì)列  
dispatch_get_global_queue
// 主隊(duì)列,在主線程中運(yùn)行,因?yàn)橹骶€程只有一個(gè),所以這是一個(gè)串行隊(duì)列  
dispatch_get_main_queue
// 除此之外,還可以自己生成隊(duì)列
// 串行隊(duì)列(串行隊(duì)列每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)一個(gè)接一個(gè)按順序執(zhí)行)
dispatch_queue_create("隊(duì)列名", DISPATCH_QUEUE_SERIAL)
// 并行隊(duì)列(雖然并發(fā)隊(duì)列可以開啟多個(gè)線程,并且同時(shí)執(zhí)行多個(gè)任務(wù)。但是因?yàn)楸旧聿荒軇?chuàng)建新線程)
dispatch_queue_create("隊(duì)列名", DISPATCH_QUEUE_CONCURRENT)
// 接下來(lái)是同步與異步線程的創(chuàng)建:
dispatch_sync(..., ^(block)) // 同步線程
dispatch_async(..., ^(block)) // 異步線程
//串行隊(duì)列+同步線程不會(huì)開啟新線程

1.串行隊(duì)列:任務(wù)按順序執(zhí)行,一個(gè)執(zhí)行完畢執(zhí)行下一個(gè)任務(wù),無(wú)論同步任務(wù)或是異步任務(wù),因?yàn)榇嘘?duì)列 執(zhí)行同步任務(wù)不開辟線程,執(zhí)行異步任務(wù)開辟最多開辟一條線程,所以必須順序執(zhí)行。
2.并行隊(duì)列的特點(diǎn),執(zhí)行異步任務(wù)具備開辟多條線程的能力,執(zhí)行同步任務(wù),順序執(zhí)行。因?yàn)闆]有開辟線程。
3.開不開線程,取決的任務(wù),同步不開新線程,異步開新線程。
4.開幾條線程取決于隊(duì)列,串行隊(duì)列開一條線程,并行隊(duì)列在執(zhí)行多個(gè)異步任務(wù)時(shí)會(huì)開辟多條線程。

dispatch_group 隊(duì)列組

分別異步執(zhí)行2個(gè)耗時(shí)任務(wù),然后當(dāng)2個(gè)耗時(shí)任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。這時(shí)候我們可以用到 GCD 的隊(duì)列組。

    //第一步創(chuàng)建隊(duì)列
    dispatch_queue_t customQuue = dispatch_queue_create("隊(duì)列名稱", DISPATCH_QUEUE_CONCURRENT);
     //第二步創(chuàng)建組
    dispatch_group_t customGroup = dispatch_group_create();
    //第三步添加任務(wù)
    dispatch_group_async(customGroup, customQuue, ^{
        NSLog(@"吃飯");
    });
    dispatch_group_async(customGroup, customQuue, ^{
        NSLog(@"洗澡");
    });
    //第四步通知
    dispatch_group_notify(customGroup, dispatch_get_main_queue(), ^{
        NSLog(@"任務(wù)完成");
    });

   //dispatch_group_wait暫停當(dāng)前線程(**阻塞當(dāng)前線程**),等待指定的 group 中的任務(wù)執(zhí)行完成后,才會(huì)往下繼續(xù)執(zhí)行。
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);


   //這里的dispatch_group_enter、dispatch_group_leave組合,其實(shí)等同于dispatch_group_async。
    dispatch_group_enter(customGroup);
    dispatch_async(customQuue, ^{
        // 追加任務(wù)1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
        dispatch_group_leave(group);
    });

dispatch_semaphore 信號(hào)量

GCD 中的信號(hào)量是指 Dispatch Semaphore,是持有計(jì)數(shù)的信號(hào)。類似于過(guò)高速路收費(fèi)站的欄桿。可以通過(guò)時(shí),打開欄桿,不可以通過(guò)時(shí),關(guān)閉欄桿。在 Dispatch Semaphore 中,使用計(jì)數(shù)來(lái)完成這個(gè)功能,計(jì)數(shù)為0時(shí)等待,不可通過(guò)。計(jì)數(shù)為1或大于1時(shí),計(jì)數(shù)減1且不等待,可通過(guò)。
Dispatch Semaphore 提供了三個(gè)函數(shù)。

1.dispatch_semaphore_create:創(chuàng)建一個(gè)Semaphore并初始化信號(hào)的總量
2.dispatch_semaphore_signal:發(fā)送一個(gè)信號(hào),讓信號(hào)總量加1
3.dispatch_semaphore_wait:可以使總信號(hào)量減1,當(dāng)信號(hào)總量為0時(shí)就會(huì)一直等待(阻塞所在線程),否則就可以正常執(zhí)行。

注意:信號(hào)量的使用前提是:想清楚你需要處理哪個(gè)線程等待(阻塞),又要哪個(gè)線程繼續(xù)執(zhí)行,然后使用信號(hào)量。
Dispatch Semaphore 在實(shí)際開發(fā)中主要用于:
保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
保證線程安全,為線程加鎖

上代碼

/**
 * 線程安全:使用 semaphore 加鎖
 * 初始化火車票數(shù)量、賣票窗口(線程安全)、并開始賣票
 */
- (void)initTicketStatusSave {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    NSLog(@"semaphore---begin");
    
    semaphoreLock = dispatch_semaphore_create(1);
    
    self.ticketSurplusCount = 50;
    // queue1 代表北京火車票售賣窗口
    dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
    // queue2 代表上?;疖嚻笔圪u窗口
    dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
    
    __weak typeof(self) weakSelf = self;
    dispatch_async(queue1, ^{
        [weakSelf saleTicketSafe];
    });
    dispatch_async(queue2, ^{
        [weakSelf saleTicketSafe];
    });
}

/**
 * 售賣火車票(線程安全)
 */
- (void)saleTicketSafe {
    while (1) {
        // 相當(dāng)于加鎖
        dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
        if (self.ticketSurplusCount > 0) {  //如果還有票,繼續(xù)售賣
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        } else { //如果已賣完,關(guān)閉售票窗口
            NSLog(@"所有火車票均已售完");
            // 相當(dāng)于解鎖
            dispatch_semaphore_signal(semaphoreLock);
            break;
        }
        // 相當(dāng)于解鎖
        dispatch_semaphore_signal(semaphoreLock);
    }
}
輸出結(jié)果為:
2018-02-23 22:32:19.814232+0800 YSC-GCD-demo[20862:5290531] currentThread---<NSThread: 0x6000000783c0>{number = 1, name = main}
2018-02-23 22:32:19.814412+0800 YSC-GCD-demo[20862:5290531] semaphore---begin
2018-02-23 22:32:19.814837+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):49 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
2018-02-23 22:32:20.017745+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):48 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:20.222039+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):47 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
......
2018-02-23 22:32:29.024817+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):4 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:29.230110+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):3 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
2018-02-23 22:32:29.433615+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):2 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:29.637572+0800 YSC-GCD-demo[20862:5290687] 剩余票數(shù):1 窗口:<NSThread: 0x6040002709c0>{number = 3, name = (null)}
2018-02-23 22:32:29.840234+0800 YSC-GCD-demo[20862:5290689] 剩余票數(shù):0 窗口:<NSThread: 0x60000046c640>{number = 4, name = (null)}
2018-02-23 22:32:30.044960+0800 YSC-GCD-demo[20862:5290687] 所有火車票均已售完
2018-02-23 22:32:30.045260+0800 YSC-GCD-demo[20862:5290689] 所有火車票均已售完

柵欄函數(shù)dispatch_barrier_async

dispatch_barrier_async函數(shù),在進(jìn)程管理中起到一個(gè)柵欄的作用,它等待所有位于barrier函數(shù)之前的操作執(zhí)行完畢后執(zhí)行,并且在barrier函數(shù)執(zhí)行之后,barrier函數(shù)之后的操作才會(huì)得到執(zhí)行,該函數(shù)需要同dispatch_queue_create函數(shù)生成的DISPATCH_QUEUE_CONCURRENT隊(duì)列一起使用。
柵欄函數(shù)的這個(gè)特點(diǎn),使得它非常適合用于做多讀單寫讀寫鎖。比如說(shuō)對(duì)于一個(gè)數(shù)據(jù),可以多線程讀取,但是只能單線程修改,就非常適合用柵欄函數(shù)dispatch_barrier和同步函數(shù)dispatch_sync配合并行隊(duì)列做數(shù)據(jù)的讀寫安全機(jī)制。

柵欄函數(shù)使用注意事項(xiàng)

為什么不能跟全局并行隊(duì)列配合使用呢?
原因在于全局隊(duì)列屬于系統(tǒng)創(chuàng)建并管理,這個(gè)隊(duì)列不止我們app在用,系統(tǒng)也在用。里面很多涉及到系統(tǒng)自身相關(guān)的操作,一旦我們外部app阻塞這個(gè)隊(duì)列,有可能會(huì)影響系統(tǒng)相關(guān)的操作。因此柵欄函數(shù)對(duì)全局(globa)并行隊(duì)列的操作是無(wú)效的 (dispatch_get_global_queue)

在
dispatch_queue_t  線程名字 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
中
dispatch_async  = dispatch_barrier_async

線程加鎖

加鎖方式一 dispatch_semaphore 自旋鎖
加鎖方式二 @synchronized 互斥鎖 @synchronized(self) { ---}
加鎖方式三 NSLock --- [_lock lock]; [_lock unlock];

swfit待更新

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

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

  • iOS中多線程 首先看一道面試題 iOS中多線程有哪些實(shí)現(xiàn)方案? iOS中,多線程一般有三種方案GCD、NSOpe...
    木子奕閱讀 723評(píng)論 0 1
  • 這篇會(huì)整理GCD常用的API 目錄1、dispatch_after2、dispatch_apply3、dispat...
    37b32340bcbc閱讀 561評(píng)論 0 0
  • 1.GCD隊(duì)列有哪幾種類型?有哪幾種隊(duì)列? GCD隊(duì)列分為串行隊(duì)列、并行隊(duì)列兩種類型;隊(duì)列有主串行隊(duì)列、全局并行隊(duì)...
    oc123閱讀 1,833評(píng)論 0 7
  • GCD API函數(shù)整理 GCD(Grand Central Dispatch)是異步執(zhí)行任務(wù)的技術(shù)之一,一般將應(yīng)用...
    凌巔閱讀 417評(píng)論 0 0
  • GCD多線程整理學(xué)習(xí) GCD簡(jiǎn)介 iOS中多核編程的解決方法。 主要用于優(yōu)化應(yīng)用程序以及支持多核處理器以及其他對(duì)稱...
    jay_丶閱讀 220評(píng)論 0 0

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