iOS 多線程入門03--GCD

一.什么是GCD?

1.GCD (Grand Central Dispatch) 是Apple公司開發(fā)的一種技術(shù),它旨在優(yōu)化多核環(huán)境中的并發(fā)操作并取代傳統(tǒng)多線程的編程模式。
2.GCD是基于C語言的線程管理方案,使用者無需過多參與線程的管理,只需要將想要執(zhí)行的代碼,添加到想要添加的調(diào)度隊(duì)列即可。
3.GCD主要用在后臺(tái)執(zhí)行較慢任務(wù);延遲執(zhí)行任務(wù);以及在后臺(tái)任務(wù)中,切換回主線程,更新UI。


二.GCD的優(yōu)勢(shì)

GCD是蘋果公司為多核的并行運(yùn)算提出的解決方案。
GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)。
GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)。
程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼。


三.任務(wù)和隊(duì)列

3.1 隊(duì)列

隊(duì)列:用于存放要執(zhí)行的任務(wù)。隊(duì)列是一種特殊的線性表,采用FIFO(先進(jìn)先出)的原則,即新任務(wù)總是被插入到隊(duì)列的末尾,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)。在 GCD中有兩種隊(duì)列:

  • 串行隊(duì)列(Serial Dispatch Queue)

一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)

  • 并發(fā)隊(duì)列(Concurrent Dispatch Queue)

可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async) 函數(shù)下才有效

DISPATCH_QUEUE_SERIAL: 表示串行,也可以使用NULL表示,因?yàn)楹甓x定義的為NULL。
DISPATCH_QUEUE_CONCURRENT:表示并發(fā)

3.2 任務(wù)

任務(wù):在線程中執(zhí)行的操作,GCD中就是指Block中的代碼。任務(wù)有兩種方式:同步執(zhí)行(sync)和異步執(zhí)行(async)。區(qū)別在于是否會(huì)創(chuàng)建新的線程。同步和異步的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程,直到Block中的任務(wù)執(zhí)行完畢。

  • 同步執(zhí)行:當(dāng)前任務(wù)不完成,不會(huì)執(zhí)行下個(gè)任務(wù)。
  • 異步執(zhí)行:當(dāng)前任務(wù)不完成,不會(huì)等待,同樣可以執(zhí)行下個(gè)任務(wù)。

四.GCD的串行隊(duì)列,并發(fā)隊(duì)列和全局隊(duì)列

各種隊(duì)列的執(zhí)行效果:


image.png

4.1并發(fā)隊(duì)列

1.并發(fā)隊(duì)列,同步任務(wù)(不會(huì)開啟新的線程,并發(fā)隊(duì)列失去了并發(fā)的功能。)
//1.創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_CONCURRENT);
    
    //2.添加任務(wù)到隊(duì)列中執(zhí)行
    for (int i=0; i<10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@ %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"come here");
總結(jié):不會(huì)開啟線程,并且會(huì)順序執(zhí)行。
2.并發(fā)隊(duì)列,異步任務(wù)(執(zhí)行較慢的任務(wù),例如大量計(jì)算,網(wǎng)絡(luò)請(qǐng)求等。)
//1.創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_CONCURRENT);
    
//2.添加任務(wù)到隊(duì)列中執(zhí)行
    for (int i=0; i<10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d",[NSThread currentThread],i);
        });
    }
    
NSLog(@"come here");
總結(jié):會(huì)開啟線程,不會(huì)順序執(zhí)行。

4.2串行隊(duì)列

1.串行隊(duì)列,同步任務(wù)(在新線程中執(zhí)行任務(wù),并且等待線程執(zhí)行完畢再向后執(zhí)行,幾乎不用)
    //1.創(chuàng)建串行隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_SERIAL);
    
    //2.添加任務(wù)到隊(duì)列中執(zhí)行
    for (int i=0; i<10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@ %d",[NSThread currentThread],i);
        });
    }
2.串行隊(duì)列,異步任務(wù)
dispatch_queue_t queue = dispatch_queue_create("gcd", DISPATCH_QUEUE_SERIAL);

for (int i=0; i<10; i++) {
      NSLog(@"%d-----",i); //主線程,會(huì)打印完9之后,才會(huì)執(zhí)行come here
      dispatch_async(queue, ^{
          NSLog(@"%@ %d",[NSThread currentThread],i); //子線程
      });
}
    NSLog(@"come here"); //在主線程,和隊(duì)列沒有任何關(guān)系
2021-03-23 23:35:59.401760+0800 多線程Demo[32679:1746588] 0-----
2021-03-23 23:35:59.401965+0800 多線程Demo[32679:1746588] 1-----
2021-03-23 23:35:59.402048+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 0
2021-03-23 23:35:59.402079+0800 多線程Demo[32679:1746588] 2-----
2021-03-23 23:35:59.402199+0800 多線程Demo[32679:1746588] 3-----
2021-03-23 23:35:59.402266+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 1
2021-03-23 23:35:59.402307+0800 多線程Demo[32679:1746588] 4-----
2021-03-23 23:35:59.402397+0800 多線程Demo[32679:1746588] 5-----
2021-03-23 23:35:59.402404+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 2
2021-03-23 23:35:59.402680+0800 多線程Demo[32679:1746588] 6-----
2021-03-23 23:35:59.402908+0800 多線程Demo[32679:1746588] 7-----
2021-03-23 23:35:59.403205+0800 多線程Demo[32679:1746588] 8-----
2021-03-23 23:35:59.403466+0800 多線程Demo[32679:1746588] 9-----
2021-03-23 23:35:59.403737+0800 多線程Demo[32679:1746588] come here
2021-03-23 23:35:59.404267+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 3
2021-03-23 23:35:59.404670+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 4
2021-03-23 23:35:59.405344+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 5
2021-03-23 23:35:59.405665+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 6
2021-03-23 23:35:59.406199+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 7
2021-03-23 23:35:59.407880+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 8
2021-03-23 23:35:59.408041+0800 多線程Demo[32679:1746641] <NSThread: 0x6000013242c0>{number = 7, name = (null)} 9
  • 首先肯定是打印完9之后,才會(huì)打印come here,因?yàn)槎际窃谥骶€程,然后打印數(shù)字和打印線程是交叉執(zhí)行的。
總結(jié):會(huì)開啟新的線程,任務(wù)會(huì)順序完成。

4.3主隊(duì)列

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
  • 總結(jié):不會(huì)開啟新線程,串行執(zhí)行任務(wù)。
    dispatch_sync(dispatch_get_main_queue(), ^{
          NSLog(@“在同步主線程中執(zhí)行,慎用,否則會(huì)死鎖”);
    });
  • 總結(jié):使用sync函數(shù)往當(dāng)前串行隊(duì)列中添加任務(wù),會(huì)卡住當(dāng)前的串行隊(duì)列(產(chǎn)生死鎖)

五.GCD的其他用法以及注意事項(xiàng)

5.1異步主線程(用于在后臺(tái)線程的任務(wù)將要完成時(shí),切換到主線程更新UI)(不會(huì)開新線程)
 //主隊(duì)列是專門負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊(duì)列 --> 不會(huì)開線程
 dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"在異步主線程中執(zhí)行");
    });
// 執(zhí)?耗時(shí)的異步操作...
    dispatch_async( dispatch_get_global_queue(0, 0), ^{ //請(qǐng)求數(shù)據(jù)
       
        dispatch_async(dispatch_get_main_queue(), ^{ // 回到主線程,執(zhí)?UI刷新操作
            //對(duì)圖片或別的操作進(jìn)行賦值等,回到主線程

        });
    });
5.2全局隊(duì)列(相當(dāng)于并發(fā)隊(duì)列)
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
5.3延時(shí)執(zhí)行線程
    //延遲
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"我在五秒后打印");
    });
5.4單例模式(只執(zhí)行一次)
static GGT_Singleton *singleton = nil;  //在.m中保留一個(gè)全局的static的實(shí)例


+ (GGT_Singleton *)sharedSingleton {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [[GGT_Singleton alloc]init];
    });
    
    return singleton;
}
5.5隊(duì)列組

如果有三個(gè)異步操作(例:網(wǎng)絡(luò)請(qǐng)求ABC),想在三個(gè)請(qǐng)求全部執(zhí)行完返回結(jié)果后,再做其他操作。
1、這三個(gè)網(wǎng)絡(luò)請(qǐng)求,執(zhí)行無序,并發(fā)執(zhí)行。
2、不論返回失敗還是成功都算返回結(jié)果。
如何處理?

如果滿足上面的需求,需要使用GCD里面的dispatch_group_enter和dispatch_group_leave來實(shí)現(xiàn)。
dispatch_group_enter:通知 group,下個(gè)任務(wù)要放入 group 中執(zhí)行了。
dispatch_group_leave:通知 group,任務(wù)成功完成,要移除,與enter()成對(duì)出現(xiàn)。
dispatch_group_notify:只要任務(wù)組完成才會(huì)調(diào)用,不完成不會(huì)調(diào)用。

    dispatch_group_t group =  dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_enter(group);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"1   %@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"2   %@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的異步操作都執(zhí)行完畢后,回到主線程...
        NSLog(@"結(jié)束");
    });
2022-02-11 18:18:51.877309+0800 TestDemo[14721:238852] 1   <NSThread: 0x6000026d6d00>{number = 3, name = (null)}
2022-02-11 18:18:53.071275+0800 TestDemo[14721:238852] 2   <NSThread: 0x6000026d6d00>{number = 3, name = (null)}
2022-02-11 18:18:53.071532+0800 TestDemo[14721:238763] 結(jié)束
5.6快速迭代(使用dispatch_apply函數(shù)能進(jìn)行快速迭代遍歷)
 dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){ //%zu用來輸出size_t 類型
        // 執(zhí)行10次代碼,index順序不確定
        NSLog(@"%zu",index);
    });
5.7 iOS中的多讀單寫安全方案
//初始化隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("rw_queue",DISPATCH_QUEUE_CONCURRENT);
    
//讀
dispatch_async(queue, ^{
});
    
//寫
dispatch_barrier_async(queue, ^{
});


六.小結(jié)

  • 同步任務(wù)死鎖:當(dāng)前是在主線程,讓主隊(duì)列執(zhí)行同步任務(wù)!
    //主隊(duì)列是專門負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊(duì)列 --> 不會(huì)開線程
    //1.隊(duì)列 --> 已啟動(dòng)主線程,就可以獲取主隊(duì)列
    dispatch_queue_t q = dispatch_get_main_queue();
    
    //2.同步任務(wù)  ==> 死鎖
    dispatch_sync(q, ^{
        NSLog(@"能來嗎? ");
    });
    NSLog(@"come here");

解決辦法:

  void (^task)() = ^{
        NSLog(@"這里%@",[NSThread currentThread]);
        //1.隊(duì)列 --> 已啟動(dòng)主線程,就可以獲取主隊(duì)列
        dispatch_queue_t q = dispatch_get_main_queue();
        
        //2.同步任務(wù)
        dispatch_sync(q, ^{
            NSLog(@"能來嗎? %@",[NSThread currentThread]);
        });
        
        NSLog(@"come here");
    };
    
    dispatch_async(dispatch_get_global_queue(0, 0), task);
  • 競爭&同步:兩個(gè)線程搶奪同一個(gè)資源,就會(huì)競爭,為了防止競爭,一個(gè)線程擁有資源的時(shí)候,會(huì)對(duì)資源加鎖,另一個(gè)線程就要等待解鎖以后再擁有這個(gè)資源,這叫同步。

  • 死鎖:兩個(gè)線程互相等待對(duì)方釋放資源。

  • 主線程&后臺(tái)線程:主線程也叫前臺(tái)線程,程序啟動(dòng)的默認(rèn)線程,操作UI的線程。后臺(tái)線程,即非主線程,用于不影響主線程的完成一些任務(wù)。

  • 同步&異步:同步執(zhí)行線程,等待新線程執(zhí)行完以后,再繼續(xù)執(zhí)行當(dāng)前線程,很少用到。異步執(zhí)行線程,在執(zhí)行新線程的同時(shí),繼續(xù)執(zhí)行當(dāng)前線程,常用。

iOS 多線程入門01--概念知識(shí)
iOS 多線程入門02--NSThread
iOS 多線程入門04--NSOperation

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 多線程 在iOS開發(fā)中為提高程序的運(yùn)行效率會(huì)將比較耗時(shí)的操作放在子線程中執(zhí)行,iOS系統(tǒng)進(jìn)程默認(rèn)啟動(dòng)一個(gè)主線程,用...
    郭豪豪閱讀 2,719評(píng)論 0 4
  • 1. GCD簡介 什么是GCD呢?我們先來看看百度百科的解釋簡單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 495評(píng)論 0 0
  • 原創(chuàng)文章 轉(zhuǎn)載請(qǐng)注明出處, 謝謝! (~ o ~)Y 本文思維導(dǎo)圖 GCD是什么 全稱是 Grand Centra...
    Jimmy_P閱讀 4,807評(píng)論 10 66
  • 從哪說起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個(gè)最簡單的問題,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,902評(píng)論 1 17
  • 之前給大家介紹過一款名叫“碩鼠”的網(wǎng)頁視頻下載工具(沒看過的朋友請(qǐng)點(diǎn)擊閱讀原文查看),相信大家對(duì)如何下載網(wǎng)頁的視頻...
    緣小異閱讀 525評(píng)論 0 0

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