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)度隊列即可。
3.GCD主要用在后臺執(zhí)行較慢任務(wù)嗡午;延遲執(zhí)行任務(wù)囤躁;以及在后臺任務(wù)中,切換回主線程荔睹,更新UI狸演。


二.GCD的優(yōu)勢

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


三.任務(wù)和隊列

3.1 隊列

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

  • 串行隊列(Serial Dispatch Queue)

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

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

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

DISPATCH_QUEUE_SERIAL: 表示串行蕊蝗,也可以使用NULL表示仅乓,因為宏定義定義的為NULL。
DISPATCH_QUEUE_CONCURRENT:表示并發(fā)

3.2 任務(wù)

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

  • 同步執(zhí)行:當(dāng)前任務(wù)不完成,不會執(zhí)行下個任務(wù)紧显。
  • 異步執(zhí)行:當(dāng)前任務(wù)不完成讲衫,不會等待,同樣可以執(zhí)行下個任務(wù)孵班。

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

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


image.png

4.1并發(fā)隊列

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

4.2串行隊列

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

for (int i=0; i<10; i++) {
      NSLog(@"%d-----",i); //主線程,會打印完9之后承冰,才會執(zhí)行come here
      dispatch_async(queue, ^{
          NSLog(@"%@ %d",[NSThread currentThread],i); //子線程
      });
}
    NSLog(@"come here"); //在主線程华弓,和隊列沒有任何關(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之后,才會打印come here困乒,因為都是在主線程寂屏,然后打印數(shù)字和打印線程是交叉執(zhí)行的。
總結(jié):會開啟新的線程娜搂,任務(wù)會順序完成迁霎。

4.3主隊列

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

五.GCD的其他用法以及注意事項

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

        });
    });
5.2全局隊列(相當(dāng)于并發(fā)隊列)
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
5.3延時執(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中保留一個全局的static的實例


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

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

如果滿足上面的需求非区,需要使用GCD里面的dispatch_group_enter和dispatch_group_leave來實現(xiàn)瓜挽。
dispatch_group_enter:通知 group,下個任務(wù)要放入 group 中執(zhí)行了。
dispatch_group_leave:通知 group,任務(wù)成功完成,要移除,與enter()成對出現(xiàn)征绸。
dispatch_group_notify:只要任務(wù)組完成才會調(diào)用,不完成不會調(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中的多讀單寫安全方案
//初始化隊列
dispatch_queue_t queue = dispatch_queue_create("rw_queue",DISPATCH_QUEUE_CONCURRENT);
    
//讀
dispatch_async(queue, ^{
});
    
//寫
dispatch_barrier_async(queue, ^{
});


六.小結(jié)

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

解決辦法:

  void (^task)() = ^{
        NSLog(@"這里%@",[NSThread currentThread]);
        //1.隊列 --> 已啟動主線程,就可以獲取主隊列
        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);
  • 競爭&同步:兩個線程搶奪同一個資源缸榄,就會競爭渤弛,為了防止競爭,一個線程擁有資源的時候甚带,會對資源加鎖她肯,另一個線程就要等待解鎖以后再擁有這個資源,這叫同步鹰贵。

  • 死鎖:兩個線程互相等待對方釋放資源晴氨。

  • 主線程&后臺線程:主線程也叫前臺線程,程序啟動的默認(rèn)線程碉输,操作UI的線程籽前。后臺線程,即非主線程敷钾,用于不影響主線程的完成一些任務(wù)枝哄。

  • 同步&異步:同步執(zhí)行線程,等待新線程執(zhí)行完以后阻荒,再繼續(xù)執(zhí)行當(dāng)前線程挠锥,很少用到。異步執(zhí)行線程侨赡,在執(zhí)行新線程的同時蓖租,繼續(xù)執(zhí)行當(dāng)前線程纱控,常用。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末菜秦,一起剝皮案震驚了整個濱河市甜害,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌球昨,老刑警劉巖尔店,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異主慰,居然都是意外死亡嚣州,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門共螺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來该肴,“玉大人,你說我怎么就攤上這事藐不≡群澹” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵雏蛮,是天一觀的道長涎嚼。 經(jīng)常有香客問我,道長挑秉,這世上最難降的妖魔是什么法梯? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮犀概,結(jié)果婚禮上立哑,老公的妹妹穿的比我還像新娘。我一直安慰自己姻灶,他們只是感情好铛绰,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著木蹬,像睡著了一般至耻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上镊叁,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天尘颓,我揣著相機與錄音,去河邊找鬼晦譬。 笑死疤苹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的敛腌。 我是一名探鬼主播卧土,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惫皱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尤莺?” 一聲冷哼從身側(cè)響起旅敷,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颤霎,沒想到半個月后媳谁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡友酱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年晴音,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缔杉。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡锤躁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出或详,到底是詐尸還是另有隱情系羞,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布鸭叙,位于F島的核電站觉啊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沈贝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一勋乾、第九天 我趴在偏房一處隱蔽的房頂上張望宋下。 院中可真熱鬧,春花似錦辑莫、人聲如沸学歧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽枝笨。三九已至,卻和暖如春揭蜒,著一層夾襖步出監(jiān)牢的瞬間横浑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工屉更, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徙融,地道東北人。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓瑰谜,卻偏偏與公主長得像欺冀,于是被迫代替她去往敵國和親树绩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

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

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