淺讀iOS多線程之GCD

??說來慚愧,學(xué)習(xí)iOS那么久,關(guān)于GCD都是直接看網(wǎng)上的資料,還沒有系統(tǒng)的看過官方文檔,所以打算好好讀一讀官方文檔,加深自己對GCD的了解。

什么是GCD

什么是GCD呢?
從《Objective-C高級編程 iOS與OS X多線程和內(nèi)存管理》一書中這樣介紹的:

Grand Central Dispatch(GCD)是異步執(zhí)行任務(wù)的技術(shù)之一。開發(fā)者只需要定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)腄ispatch Queue 中, GCD就能生成必要的線程并計劃執(zhí)行任務(wù)。由于線程管理是作為系統(tǒng)的一部分來實現(xiàn)的,因此可統(tǒng)一管理,也可執(zhí)行任務(wù),這樣久比以前的線程更有效率。也就是說,GCD用我們難以置信的非常簡潔技術(shù)方法,實現(xiàn)了極為復(fù)雜繁瑣的多線程編程。

但是我在看官方文檔中并沒有看到這段話,估計是更新了吧。最新的官網(wǎng)上是這么說的:

Grand Central Dispatch(GCD)包含了用來增強系統(tǒng)對多核硬件的并發(fā)執(zhí)行能力的語言特性、runtime庫和系統(tǒng)加強包。GCD在系統(tǒng)級別上運行,可以更好的適應(yīng)所有運行的應(yīng)用程序的需求,并以一種平衡的方式將它們和可用的資源相匹配。

總體來說,GCD使用方便且高效,但是使用的時候需要注意,避免線程阻塞、死鎖等狀況發(fā)生。

GCD的API

1.Dispatch Queue

Dispatch Queue 是一個輕量級的對象,你只需提交你想執(zhí)行任務(wù)的代碼塊。Dispatch Queue 按照FIFO(先進先出)順序調(diào)用提交給它的代碼塊。一個串型隊列一次只能調(diào)用一個塊,但是不同的隊列可以同時調(diào)用它們自己的代碼塊。

Dispatch Queue 的類型分為兩種:DISPATCH_QUEUE_SERIAL 和 DISPATCH_QUEUE_CONCURRENT。

Dispatch Queue Type 說明
DISPATCH_QUEUE_SERIAL 串型隊列,每次只能執(zhí)行一個任務(wù)
DISPATCH_QUEUE_CONCURRENT 并行隊列,可以同時執(zhí)行多個任務(wù)

2.獲得Dispatch Queue

我們了解了Dispatch Queue的類型,那么如何獲得DispatchQueue呢?我們有兩張方法獲取Dispatch Queue:1.通過dispatch_queue_create 自己創(chuàng)建 2.獲取系統(tǒng)提供的Dispatch Queue(Main Dispatch Queue & Global Dispatch Queue)。

2.1 dispatch_queue_create

我們可以直接通過dispatch_queue_create函數(shù)生成Dispatch Queue

       //第一個參數(shù):queue的唯一標(biāo)識 ;第二個參數(shù):隊列的類型 ,為NULL是默認為DISPATCH_QUEUE_SERIAL
       dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t  _Nullable attr#>)
       dispatch_queue_t queue = dispatch_queue_create("com.serial0.myqueue", NULL);
       dispatch_queue_t serialQueue = dispatch_queue_create("com.serial1.myqueue", DISPATCH_QUEUE_SERIAL);
       dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrentQueue.myqueue", DISPATCH_QUEUE_CONCURRENT);

其中dispatch_queue_create函數(shù)的第一個參數(shù)是創(chuàng)建的隊列的唯一標(biāo)識,官方文檔推薦我們使用反向dns的命名格式(com.examle.myqueue)。你可以將標(biāo)識設(shè)置為NULL,但是不推薦,因為給隊列添加標(biāo)識,能夠在調(diào)試的時候清楚的知道當(dāng)前的執(zhí)行隊列,還能在crashLog中給出提示。 你也可以通過dispatch_queue_get_label函數(shù)獲取當(dāng)前隊列的標(biāo)識。第二個參事設(shè)置隊列的類型,置NULL為DISPATCH_QUEUE_SERIAL類型。

2.2 Main Dispatch Queue & Global Dispatch Queue

Main Dispatch Queue 為主線程,有且僅有一個,所以主線程是serial Dispatch Queue。追加到主線程到任務(wù)在主線程的RunLoop中執(zhí)行,所以用戶界面的UI更新這些必須放到主線程中,而一些耗時的操作盡量避免放到主線程。
Global Dispatch Queue 為全局線程,是Concurrent Dispatch Queue,有四個優(yōu)先級,分別為:

  • 高優(yōu)先級(DISPATCH_QUEUE_PRIORITY_HIGH)
  • 默認優(yōu)先級(DISPATCH_QUEUE_PRIORITY_DEFAULT)
  • 低優(yōu)先級(DISPATCH_QUEUE_PRIORITY_LOW)
  • 后臺優(yōu)先級(DISPATCH_QUEUE_PRIORITY_BACKGROUND)

系統(tǒng)提供的Dispatch Queue的總結(jié)如下:

名稱 類型 說明
Main Dispatch Queue Serial Dispatch Queue 主線程執(zhí)行
Global Dispatch Queue(High Priority) Concurrent Dispatch Queue 高優(yōu)先級
Global Dispatch Queue(Default Priority) Concurrent Dispatch Queue 默認優(yōu)先級
Global Dispatch Queue(Low Priority) Concurrent Dispatch Queue 低優(yōu)先級
Global Dispatch Queue(Background Priority) Concurrent Dispatch Queue 后臺優(yōu)先級

系統(tǒng)Dispatch Queue 獲取如下:

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

GCD使用

我們了解了GCD,也知道獲取Dispatch Queue 的方法,那么如何使用呢?系統(tǒng)的API里有許多方法,這里先介紹最常見的兩種方法dispatch_async和dispatch_sync。

dispatch_async 異步執(zhí)行

異步執(zhí)行,不受其他代碼塊的影響,一旦放入隊列中就執(zhí)行。

    dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(defaultQueue, ^{
       NSLog(@"actionA:%@", [NSThread currentThread]);
    });
    dispatch_async(defaultQueue, ^{
        NSLog(@"actionB:%@", [NSThread currentThread]);
    });
    dispatch_async(defaultQueue, ^{
        NSLog(@"actionC:%@", [NSThread currentThread]);
    });
    dispatch_async(defaultQueue, ^{
        NSLog(@"actionD:%@", [NSThread currentThread]);
    });

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

2018-03-22 15:48:10.332231+0800 LCChat[93644:7440393] actionC:<NSThread: 0x60c000279f40>{number = 5, name = (null)}
2018-03-22 15:48:10.332232+0800 LCChat[93644:7440394] actionA:<NSThread: 0x60c000279bc0>{number = 3, name = (null)}
2018-03-22 15:48:10.332242+0800 LCChat[93644:7440395] actionD:<NSThread: 0x604000276c80>{number = 6, name = (null)}
2018-03-22 15:48:10.332252+0800 LCChat[93644:7440397] actionB:<NSThread: 0x608000271040>{number = 4, name = (null)}

說明異步執(zhí)行的代碼塊順序是不可控制的。

dispatch_sync 同步執(zhí)行

    dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(defaultQueue, ^{
       NSLog(@"actionA:%@", [NSThread currentThread]);
    });
    dispatch_sync(defaultQueue, ^{
        NSLog(@"actionB:%@", [NSThread currentThread]);
    });
    dispatch_sync(defaultQueue, ^{
        NSLog(@"actionC:%@", [NSThread currentThread]);
    });
    dispatch_sync(defaultQueue, ^{
        NSLog(@"actionD:%@", [NSThread currentThread]);
    });

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

2018-03-22 15:51:18.971098+0800 LCChat[93697:7443960] actionA:<NSThread: 0x60800006cd80>{number = 1, name = main}
2018-03-22 15:51:18.971344+0800 LCChat[93697:7443960] actionB:<NSThread: 0x60800006cd80>{number = 1, name = main}
2018-03-22 15:51:18.971475+0800 LCChat[93697:7443960] actionC:<NSThread: 0x60800006cd80>{number = 1, name = main}
2018-03-22 15:51:18.971603+0800 LCChat[93697:7443960] actionD:<NSThread: 0x60800006cd80>{number = 1, name = main}

證明同步執(zhí)行遵守FIFO,必須等前一個block完成才能進行下一個block。

總結(jié)

基本的GCD的介紹和常用的GCD使用在上文中已經(jīng)大致的介紹了,其實你看官方文檔,你會發(fā)現(xiàn)GCD還有很多強大的功能,比如dispatch_once,dispatch I/Od等,這些不常用的,等有空的時候再一一細讀。

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

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

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