Objective-C之GCD多線程(一)

前言

什么是GCD?

Grand Central dispatch(GCD)是異步執(zhí)行任務(wù)的技術(shù)之一。一般將應(yīng)用程序中記述的線程管理用的代碼在系統(tǒng)級(jí)中實(shí)現(xiàn)。開發(fā)者只需要定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)膁ispatch Queue中,GCD就能生成必要的線程并計(jì)劃執(zhí)行任務(wù)。由于線程管理是作為系統(tǒng)的一部分來實(shí)現(xiàn)的,因此可以統(tǒng)一管理,也可執(zhí)行任務(wù),這樣就比以前的線程更加有效率。

什么是線程?

  • 線程(thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。
  • Mac、iPhone的操作系統(tǒng)OS X、iOS根據(jù)用戶的指示啟動(dòng)應(yīng)用程序后,首先便將包含在應(yīng)用程序中的CPU命令列配置到內(nèi)存中。CPU從應(yīng)用程序指定的地址開始,一個(gè)一個(gè)地執(zhí)行CPU命令列。由于一個(gè)CPU一次只能執(zhí)行一個(gè)命令,不能執(zhí)行某處分開的并列的兩個(gè)命令,因此通過CPU執(zhí)行的CPU命令列就是一條路走到黑。這里所說的“1個(gè)CPU執(zhí)行的CPU名列為一條路走到黑”就是“線程”。
  • 現(xiàn)在一個(gè)物理的CPU芯片實(shí)際上有64(64核)個(gè)CPU,即可以擁有多條線程。
  • 多線程編程會(huì)產(chǎn)生很多編程技術(shù)問題:數(shù)據(jù)競(jìng)爭(zhēng)、死鎖、內(nèi)存消耗等問題。
  • 想要更加了解線程、進(jìn)程等可自行參閱《操作系統(tǒng)原理》這本書。

dispatch Queue

dispatch Queue是執(zhí)行處理的等待隊(duì)列。我們可以調(diào)用dispatch_async函數(shù)等API,在Block語法中記述想執(zhí)行的處理并追加到dispatch Queue中。dispatch Queue會(huì)按照FIFO(先進(jìn)先出)執(zhí)行處理。下面的圖解釋了FIFO

通過dispatch Queue執(zhí)行處理

在dispatch Queue中存在兩種dispatch Queue,一種是等待現(xiàn)在執(zhí)行中的Serial dispatch Queue,另一種是不等待直接執(zhí)行的Concurrent dispatch Queue。

dispatch Queue種類 說明
Serial dispatch Queue 等待現(xiàn)在執(zhí)行中的處理結(jié)束后才開始執(zhí)行
Concurrent dispatch Queue 不等待現(xiàn)在執(zhí)行中的處理,直接開始執(zhí)行
Serial dispatch Queue
Concurrent dispatch Queue

因?yàn)镾erial dispatch Queue是等待執(zhí)行完成后才開始下一個(gè)處理。那么在有多個(gè)處理的時(shí)候,也是按照先后順序來的。

int main(int argc, const char * argv[]) {
    // 創(chuàng)建一個(gè)SerialQueue
    dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    // Serial dispatch Queue
    dispatch_async(serialdispatchQueue, ^{printf("1\n");});
    dispatch_async(serialdispatchQueue, ^{printf("2\n");});
    dispatch_async(serialdispatchQueue, ^{printf("3\n");});
    dispatch_async(serialdispatchQueue, ^{printf("4\n");});
    // 防止程序結(jié)束
    while (1) {
    }
    return 0;
}

執(zhí)行結(jié)果如下:

Serial Queue執(zhí)行結(jié)果

Concurrent dispatch Queue是不等待執(zhí)行,那么任何任務(wù)添加到Concurrent dispatch Queue后,就會(huì)立即執(zhí)行。但是Concurrent dispatch Queue不是無限制的立即執(zhí)行當(dāng)前添加的處理,當(dāng)前并行執(zhí)行的處理的數(shù)量取決于當(dāng)前系統(tǒng)的狀態(tài)。即iOS和OS X基于dispatch Queue中的處理數(shù)、CPU數(shù)、以及CPU負(fù)荷等當(dāng)前系統(tǒng)狀態(tài)來決定Concurrent dispatch Queue中并行處理數(shù)。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    // 創(chuàng)建一個(gè)Concurrent dispatch Queue
    dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);
    // Serial dispatch Queue
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"1");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"2");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"3");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"4");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"5");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"6");});
    // 防止程序結(jié)束
    while (1) {
    }
    return 0;
}

執(zhí)行結(jié)果:

Concurrent dispatch Queue執(zhí)行結(jié)果

其中執(zhí)行結(jié)果亂序是因?yàn)樘幚硖砑拥骄€程中的時(shí)候,需要等待線程執(zhí)行完成后,才開始執(zhí)行。即6個(gè)Block添加到6個(gè)線程中,6個(gè)線程里面都仍然有任務(wù),等任務(wù)執(zhí)行完成后,才開始執(zhí)行我們添加的處理。

dispatch_queue_create

在Dispatch Queue中,我們使用了dispatch_queue_create來建立一個(gè)Queue。
生成Dispatch Queue有兩種方法:

  • 通過GCD的API生成dispatch Queue
  • 獲取系統(tǒng)標(biāo)準(zhǔn)提供的dispatch Queue
    這里先說明第一種方法,第二種在Main dispatch Queue/Global dispatch Queue中說明。

使用dispatch_queue_create

    // 創(chuàng)建一個(gè)Concurrent dispatch Queue
    dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);

    // 創(chuàng)建一個(gè)Serial dispatch Queue
    dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);

dispatch_queue_create函數(shù)有2個(gè)參數(shù),第一個(gè)參數(shù)是dispatch Queue的名稱,推薦使用應(yīng)用程序ID這樣的逆序全程域名。該名稱在Xcode和Instruments的調(diào)試器中作為dispatch Queue的名稱表示。第二個(gè)參數(shù)是指定dispatch Queue的類型,如果填寫NULL則默認(rèn)為DISPATCH_QUEUE_SERIAL。
前文講到,Serial dispatch Queue同時(shí)只能執(zhí)行一個(gè)處理。但是生成多個(gè)Serial dispatch Queue時(shí),各個(gè)Serial dispatch Queue將并行執(zhí)行。4個(gè)處理追加到4個(gè)Serial dispatch Queue中,4個(gè)處理將同時(shí)執(zhí)行。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    // 創(chuàng)建一個(gè)SerialQueue
    dispatch_queue_t serialdispatchQueue1 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialdispatchQueue2 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialdispatchQueue3 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    // Serial dispatch Queue
    dispatch_async(serialdispatchQueue1, ^{NSLog(@"1");});
    dispatch_async(serialdispatchQueue2, ^{NSLog(@"2");});
    dispatch_async(serialdispatchQueue3, ^{NSLog(@"3");});
    // 防止程序結(jié)束
    while (1) {
    }
    return 0;
}

執(zhí)行結(jié)果:

多個(gè)Serial dispatch Queue同時(shí)執(zhí)行

由執(zhí)行結(jié)果亂序可以知道,多個(gè)Serial dispatch Queue是并行執(zhí)行的!

dispatch_queue_create使用注意

  • 不能無限制的創(chuàng)建Serial Dispatch Queue,會(huì)消耗大量的內(nèi)存,引起大量的上下文切換,大幅度降低系統(tǒng)的響應(yīng)性能。
  • 在為了避免多個(gè)線程對(duì)同一個(gè)資源進(jìn)行操作時(shí)(數(shù)據(jù)競(jìng)爭(zhēng))使用Serial Dispatch Queue,因?yàn)槠涫褂靡粋€(gè)線程,數(shù)據(jù)安全。
  • 當(dāng)不需要顧忌數(shù)據(jù)競(jìng)爭(zhēng)問題時(shí)候,推薦使用Concurrent Dispatch Queue。因?yàn)椴还苌啥嗌?,系統(tǒng)會(huì)對(duì)其進(jìn)行管理,不用擔(dān)心Serial Dispatch Queue類似的問題。
  • 最好為每一個(gè)Dispatch Queue編寫不同的名字,否則你會(huì)在調(diào)試多線程程序的時(shí)候感覺自己仿佛是一個(gè)辣雞。
  • 關(guān)于 dispatch_retaindispatch_release 的使用
    • 如果你部署的最低目標(biāo)低于 iOS 6.0 or Mac OS X 10.8,你應(yīng)該自己管理GCD對(duì)象,使用(dispatch_retain,dispatch_release),ARC并不會(huì)去管理它們。
    • 如果你部署的最低目標(biāo)是 iOS 6.0 or Mac OS X 10.8或者更高,ARC已經(jīng)能夠管理GCD對(duì)象了,這時(shí)候,GCD對(duì)象就如同普通的OC對(duì)象一樣,不應(yīng)該使用dispatch_retain或者dispatch_release。

Main Dispatch Queue/Global Dispatch Queue

除了使用dispatch_queue_create,還可以利用系統(tǒng)標(biāo)準(zhǔn)提供的Dispatch Queue。系統(tǒng)提供了2個(gè):

  • Main Dispatch Queue,即主線程中執(zhí)行的Dispatch Queue,而主線程只有一個(gè),所以Main Dispatch Queue就是Serial Dispatch Queue.
  • Global Dispatch Queue,是所有應(yīng)用程序都能夠使用的Concurrent Dispatch Queue。所以沒有必要通過dispatch_queue_create來創(chuàng)建,直接獲取Global Dispatch Queue即可。
   /** Main Dispatch Queue的獲取 */
   dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

   /** Global Dispatch Queue(最高)的獲取方法 */
   dispatch_queue_t highGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

   /** Global Dispatch Queue(默認(rèn))的獲取方法 */
   dispatch_queue_t defaultGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

   /** Global Dispatch Queue(低)的獲取方法 */
   dispatch_queue_t losGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

   /** Global Dispatch Queue(后臺(tái),最低)的獲取方法 */
   dispatch_queue_t backgroundGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

Global Dispatch Queue

Global Dispatch Queue有4個(gè)優(yōu)先級(jí),分別是高優(yōu)先級(jí)、默認(rèn)優(yōu)先級(jí)、低優(yōu)先級(jí)和后臺(tái)優(yōu)先級(jí)。

名稱 Dispatch Queue的種類 說明
Main Dispatch Queue Serial Queue 主線程執(zhí)行
Global Dispatch Queue(High Priority)高優(yōu)先級(jí) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級(jí):最高
Global Dispatch Queue(Default Priority)默認(rèn)優(yōu)先級(jí) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級(jí):默認(rèn)
Global Dispatch Queue(Low Priority)低優(yōu)先級(jí) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級(jí):低
Global Dispatch Queue(Background Priority)后臺(tái)優(yōu)先級(jí) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級(jí):后臺(tái)(最低)

結(jié)語

  • 本文主要講常用的一些GCD的API,更多內(nèi)容,請(qǐng)等待下一周更新。
  • 此為《Objective-C 高級(jí)編程》的學(xué)習(xí)筆記。
  • 如有錯(cuò)誤,歡迎指正。
  • 如需轉(zhuǎn)載,請(qǐng)注明出處。
最后編輯于
?著作權(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)容

  • 3.1 Grand Central Dispatch(GCD)概要 3.1.1 什么是CGD Grand Cent...
    SkyMing一C閱讀 1,776評(píng)論 0 22
  • 多線程概念 線程線程指的是:1個(gè)CPU執(zhí)行的CPU命令列為一條無分叉路徑 多線程這種無分叉路徑不止一條,存在多條即...
    我系哆啦閱讀 651評(píng)論 0 5
  • 1. GCD簡(jiǎn)介 什么是GCD呢?我們先來看看百度百科的解釋簡(jiǎn)單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 495評(píng)論 0 0
  • 估計(jì)看完上一篇文章《演繹和歸納》,你該想了,這兩個(gè)概念我清楚了,但是怎么能用起來,好讓自己的思考更深刻呢? 感覺好...
    北北談教育與學(xué)習(xí)閱讀 345評(píng)論 0 0
  • 晴天碧海,微風(fēng)習(xí)習(xí)。甲板上,游人如織。舞臺(tái)上,俄羅斯小伙們的踢踏舞活力無限。蹦蹦噠噠,聲聲不息。船艙里,斑駁,...
    蘭于煙閱讀 799評(píng)論 0 0

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