iOS底層-GCD之函數(shù)與隊(duì)列

GCD簡介

GCD全稱是Grand Central Dispatch

純C語言幽歼,提供例如非常強(qiáng)大的函數(shù)

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ù)九默,不需要編寫任何線程管理代碼

【重點(diǎn)】用一句話總結(jié)GCD就是:將任務(wù)添加到隊(duì)列蒸殿,并指定任務(wù)執(zhí)行的函數(shù)

GCD核心

在日常開發(fā)中,GCD一般寫成下面這種形式:

 dispatch_async( dispatch_queue_create("com.CJL.Queue", NULL), ^{
   NSLog(@"GCD基本使用");
});

將上述代碼拆分恬总,方便我們來理解GCD的核心 主要是由 任務(wù) + 隊(duì)列 + 函數(shù) 構(gòu)成

//********GCD基礎(chǔ)寫法********
//創(chuàng)建任務(wù)
dispatch_block_t block = ^{
    NSLog(@"hello GCD");
};

//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", NULL);

//將任務(wù)添加到隊(duì)列前普,并指定函數(shù)執(zhí)行
dispatch_async(queue, block);

使用dispatch_block_t創(chuàng)建任務(wù)
使用dispatch_queue_t創(chuàng)建隊(duì)列
將任務(wù)添加到隊(duì)列,并指定執(zhí)行任務(wù)的函數(shù)dispatch_async

注意

這里的任務(wù)是指執(zhí)行操作的意思壹堰,在使用dispatch_block_t創(chuàng)建任務(wù)時(shí)拭卿,主要有以下兩點(diǎn)說明

任務(wù)使用block封裝

任務(wù)的block沒有參數(shù)也沒有返回值

函數(shù)與隊(duì)列

函數(shù)

在GCD中執(zhí)行任務(wù)的方式有兩種,同步執(zhí)行和異步執(zhí)行贱纠,分別對(duì)應(yīng) 同步函數(shù)dispatch_sync 和 異步函數(shù)dispatch_async,兩者對(duì)比如下

同步執(zhí)行峻厚,對(duì)應(yīng)同步函數(shù)dispatch_sync
必須等待當(dāng)前語句執(zhí)行完畢,才會(huì)執(zhí)行下一條語句

不會(huì)開啟線程谆焊,即不具備開啟新線程的能力

在當(dāng)前線程中執(zhí)行block任務(wù)

異步執(zhí)行惠桃,對(duì)應(yīng)異步函數(shù)dispatch_async

不用等待當(dāng)前語句執(zhí)行完畢,就可以執(zhí)行下一條語句

會(huì)開啟線程執(zhí)行block任務(wù),即具備開啟新線程的能力(但并不一定開啟新線程辜王,這個(gè)與任務(wù)所指定的隊(duì)列類型有關(guān))

異步 是 多線程 的代名詞

所以劈狐,綜上所述,兩種執(zhí)行方式的主要區(qū)別有兩點(diǎn):

是否等待隊(duì)列的任務(wù)執(zhí)行完畢

是否具備開啟新線程的能力

隊(duì)列

串行隊(duì)列 和 并發(fā)隊(duì)列

多線程中所說的隊(duì)列(Dispatch Queue)是指執(zhí)行任務(wù)的等待隊(duì)列誓禁,即用來存放任務(wù)的隊(duì)列懈息。隊(duì)列是一種特殊的線性表肾档,遵循先進(jìn)先出(FIFO)原則摹恰,即新任務(wù)總是被插入到隊(duì)尾,而任務(wù)的讀取從隊(duì)首開始讀取怒见。每讀取一個(gè)任務(wù)俗慈,則從隊(duì)列中釋放一個(gè)任務(wù),如下圖所示:

隊(duì)列圖示.jpg

在GCD中遣耍,隊(duì)列主要分為串行隊(duì)列(Serial Dispatch Queue) 和并發(fā)隊(duì)列(Concurrent Dispatch Queue)兩種闺阱,如下圖所示:


串行&&并行.jpg

串行隊(duì)列:每次只有一個(gè)任務(wù)被執(zhí)行,等待上一個(gè)任務(wù)執(zhí)行完畢再執(zhí)行下一個(gè)舵变,即只開啟一個(gè)線程(通俗理解:同一時(shí)刻只調(diào)度一個(gè)任務(wù)執(zhí)行)
使用dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);創(chuàng)建串行隊(duì)列

其中的DISPATCH_QUEUE_SERIAL也可以使用NULL表示酣溃,這兩種均表示 默認(rèn)的串行隊(duì)列

// 串行隊(duì)列的獲取方法
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.CJL.Queue", NULL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_SERIAL);

并發(fā)隊(duì)列:一次可以并發(fā)執(zhí)行多個(gè)任務(wù),即開啟多個(gè)線程纪隙,并同時(shí)執(zhí)行任務(wù)(通俗理解:同一時(shí)刻可以調(diào)度多個(gè)任務(wù)執(zhí)行)
使用dispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);創(chuàng)建并發(fā)隊(duì)列

注意:并發(fā)隊(duì)列的并發(fā)功能只有在異步函數(shù)下才有效

// 并發(fā)隊(duì)列的獲取方法
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.CJL.Queue", DISPATCH_QUEUE_CONCURRENT);

主隊(duì)列 和 全局并發(fā)隊(duì)列
在GCD中赊豌,針對(duì)這兩種隊(duì)列,分別提供了主隊(duì)列(Main Dispatch Queue)和全局并發(fā)隊(duì)列(Global Dispatch Queue)

主隊(duì)列(Main Dispatch Queue):GCD中提供的特殊的串行隊(duì)列
專門用來在主線程上調(diào)度任務(wù)的串行隊(duì)列绵咱,依賴于主線程碘饼、主Runloop,在main函數(shù)調(diào)用之前自動(dòng)創(chuàng)建

不會(huì)開啟線程

如果當(dāng)前主線程正在有任務(wù)執(zhí)行悲伶,那么無論主隊(duì)列中當(dāng)前被添加了什么任務(wù)艾恼,都不會(huì)被調(diào)度

使用dispatch_get_main_queue()獲得主隊(duì)列

通常在返回主線程 更新UI時(shí)使用

//主隊(duì)列的獲取方法
dispatch_queue_t mainQueue = dispatch_get_main_queue();

全局并發(fā)隊(duì)列(Global Dispatch Queue):GCD提供的默認(rèn)的并發(fā)隊(duì)列
為了方便程序員的使用,蘋果提供了全局隊(duì)列

在使用多線程開發(fā)時(shí)麸锉,如果對(duì)隊(duì)列沒有特殊需求钠绍,在執(zhí)行異步任務(wù)時(shí),可以直接使用全局隊(duì)列

使用dispatch_get_global_queue獲取全局并發(fā)隊(duì)列花沉,最簡單的是dispatch_get_global_queue(0, 0)

第一個(gè)參數(shù)表示隊(duì)列優(yōu)先級(jí)柳爽,默認(rèn)優(yōu)先級(jí)為DISPATCH_QUEUE_PRIORITY_DEFAULT=0,在ios9之后主穗,已經(jīng)被服務(wù)質(zhì)量(quality-of-service)取代

第二個(gè)參數(shù)使用0

//全局并發(fā)隊(duì)列的獲取方法
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);

//優(yōu)先級(jí)從高到低(對(duì)應(yīng)的服務(wù)質(zhì)量)依次為
- DISPATCH_QUEUE_PRIORITY_HIGH       -- QOS_CLASS_USER_INITIATED
- DISPATCH_QUEUE_PRIORITY_DEFAULT    -- QOS_CLASS_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW        -- QOS_CLASS_UTILITY
- DISPATCH_QUEUE_PRIORITY_BACKGROUND -- QOS_CLASS_BACKGROUND

全局并發(fā)隊(duì)列 + 主隊(duì)列 配合使用

在日常開發(fā)中泻拦,全局隊(duì)列+并發(fā)并列一般是這樣配合使用的

//主隊(duì)列 + 全局并發(fā)隊(duì)列的日常使用
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //執(zhí)行耗時(shí)操作
        dispatch_async(dispatch_get_main_queue(), ^{
            //回到主線程進(jìn)行UI操作
        });
    });

函數(shù)與隊(duì)列的不同組合

串行隊(duì)列 + 同步函數(shù)

任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的在當(dāng)前線程執(zhí)行,不會(huì)開辟新線程

串行隊(duì)列 + 同步函數(shù).png

串行隊(duì)列 + 異步函數(shù)

【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行忽媒,會(huì)開辟新線程


串行隊(duì)列 + 異步函數(shù).png

并發(fā)隊(duì)列 + 同步函數(shù)

【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行争拐,不開辟線程


并發(fā)隊(duì)列 + 同步函數(shù).png

并發(fā)隊(duì)列 + 異步函數(shù)

【任務(wù)亂序執(zhí)行】:任務(wù)執(zhí)行無順序,會(huì)開辟新線程

并發(fā)隊(duì)列 + 異步函數(shù).png

主隊(duì)列 + 同步函數(shù)

【造成死鎖】:任務(wù)相互等待,造成死鎖

主隊(duì)列 + 同步函數(shù).png

造成死鎖的原因分析如下:

主隊(duì)列有兩個(gè)任務(wù)架曹,順序?yàn)椋篘SLog任務(wù) - 同步block

執(zhí)行NSLog任務(wù)后隘冲,執(zhí)行同步Block,會(huì)將任務(wù)1(即i=1時(shí))加入到主隊(duì)列绑雄,主隊(duì)列順序?yàn)椋篘SLog任務(wù) - 同步block - 任務(wù)1

任務(wù)1的執(zhí)行需要等待同步block執(zhí)行完畢才會(huì)執(zhí)行展辞,而同步block的執(zhí)行需要等待任務(wù)1執(zhí)行完畢,所以就造成了任務(wù)互相等待的情況万牺,即造成死鎖崩潰

死鎖現(xiàn)象

主線程因?yàn)槟阃胶瘮?shù)的原因等著先執(zhí)行任務(wù)

主隊(duì)列等著主線程的任務(wù)執(zhí)行完畢再執(zhí)行自己的任務(wù)

主隊(duì)列和主線程相互等待會(huì)造成死鎖

主隊(duì)列 + 異步函數(shù)

【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行罗珍,不開辟線程

主隊(duì)列 + 異步函數(shù).png

全局并發(fā)隊(duì)列 + 同步函數(shù)

【任務(wù)按順序執(zhí)行】:任務(wù)一個(gè)接一個(gè)的執(zhí)行,不開辟新線程

全局并發(fā)隊(duì)列 + 同步函數(shù).png

全局并發(fā)隊(duì)列 + 異步函數(shù)

【任務(wù)亂序執(zhí)行】:任務(wù)亂序執(zhí)行脚粟,會(huì)開辟新線程

全局并發(fā)隊(duì)列 + 異步函數(shù).png

總結(jié)

函數(shù)與隊(duì)列圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末覆旱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子核无,更是在濱河造成了極大的恐慌扣唱,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件团南,死亡現(xiàn)場離奇詭異噪沙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吐根,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門正歼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人佑惠,你說我怎么就攤上這事朋腋。” “怎么了膜楷?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵旭咽,是天一觀的道長。 經(jīng)常有香客問我赌厅,道長穷绵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任特愿,我火速辦了婚禮仲墨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘揍障。我一直安慰自己目养,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布毒嫡。 她就那樣靜靜地躺著癌蚁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上努释,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天碘梢,我揣著相機(jī)與錄音,去河邊找鬼伐蒂。 笑死煞躬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逸邦。 我是一名探鬼主播恩沛,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼昭雌!你這毒婦竟也來了复唤?” 一聲冷哼從身側(cè)響起健田,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤烛卧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后耐量,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寺鸥,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陕见,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了局雄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡存炮,死狀恐怖炬搭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情穆桂,我是刑警寧澤宫盔,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站享完,受9級(jí)特大地震影響灼芭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜般又,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一彼绷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茴迁,春花似錦寄悯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春昔馋,著一層夾襖步出監(jiān)牢的瞬間筹吐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國打工秘遏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丘薛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓邦危,卻偏偏與公主長得像洋侨,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子倦蚪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361