【iOS】GCD-基礎(chǔ)篇

OS X Snow Leopard 和 iOS 開(kāi)始,apple公司為開(kāi)發(fā)者引入了新的多線(xiàn)程編程功能"Grand Central Dispatch"(超級(jí)派發(fā)中心,也稱(chēng)大調(diào)度中心)。


1. 概要

1.1 什么是GCD

GCD是異步執(zhí)行任務(wù)的一種技術(shù)。開(kāi)發(fā)者只需要定義需要執(zhí)行的任務(wù)并將任務(wù)提交到適當(dāng)?shù)腄ispatch Queue中,GCD就能生成必要的線(xiàn)程并有計(jì)劃的執(zhí)行任務(wù)。

dispatch_async(queue, ^{
    
    /*
     * 長(zhǎng)時(shí)間處理
     * 例如:數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)
     **/
    
    /*
     * 長(zhǎng)時(shí)間處理結(jié)束,主線(xiàn)程使用該處理結(jié)果
     **/
    
    dispatch_async(dispatch_get_main_queue(), ^{
       
        /*
         * 回到主線(xiàn)程進(jìn)行處理
         * 如:根據(jù)數(shù)據(jù)更新用戶(hù)界面
         **/
        
    });
    
});

上面的任務(wù)是GCD一種簡(jiǎn)單實(shí)現(xiàn),定義了一個(gè)block任務(wù)(長(zhǎng)時(shí)間數(shù)據(jù)庫(kù)處理任務(wù)),然后將該任務(wù)異步提交到隊(duì)列queue中,GCD根據(jù)隊(duì)列中的任務(wù)生成必要的線(xiàn)程,來(lái)執(zhí)行長(zhǎng)時(shí)間數(shù)據(jù)庫(kù)處理任務(wù),并提交新任務(wù)到主隊(duì)列(回到主線(xiàn)程)進(jìn)行UI刷新工作。

思考:還有沒(méi)有其他方式從子線(xiàn)程回到主線(xiàn)程?哪種方式好?

1.2 多線(xiàn)程編程

先來(lái)看幾個(gè)關(guān)鍵概念
線(xiàn)程:一條CPU指令連續(xù)不階段的執(zhí)行流就是線(xiàn)程。
多線(xiàn)程:多核CPU可以并列的執(zhí)行多條cpu指令流。
原生核CPU:真正意義上的多核CPU,每個(gè)核心相互獨(dú)立,擁有自己的總線(xiàn)。
封裝核CPU:簡(jiǎn)單的封裝多個(gè)單核CPU,只有一條共用總線(xiàn)。
目前為止,iphone 6s是最新的iOS平臺(tái)設(shè)備,采用的是原生雙核A9處理器。

多線(xiàn)程編程存在問(wèn)題:

  • 1.資源競(jìng)爭(zhēng):多個(gè)線(xiàn)程更新相同資源導(dǎo)致數(shù)據(jù)不一致
  • 2.死鎖:停止等待事件的線(xiàn)程會(huì)導(dǎo)致多個(gè)線(xiàn)程相互持續(xù)等待
  • 3.內(nèi)存消耗:使用太多線(xiàn)程會(huì)消耗內(nèi)存
    后面給出以上問(wèn)題解決方案。

多線(xiàn)程應(yīng)用場(chǎng)景:
主線(xiàn)程主要用來(lái)處理界面刷新,屏幕點(diǎn)擊事件等操作,長(zhǎng)時(shí)間的處理不要放在主線(xiàn)程中執(zhí)行,應(yīng)該放在其他線(xiàn)程中執(zhí)行。

2.GCD 主要 API

2.1 Dispatch Queue

開(kāi)發(fā)者要做的只是在block中定義想要執(zhí)行的任務(wù)并提交到適當(dāng)?shù)腄ispatch Queue中,Dispatch Queue是執(zhí)行處理的等待隊(duì)列,負(fù)責(zé)控制任務(wù)進(jìn)出線(xiàn)程的次序,執(zhí)行處理時(shí)遵循先進(jìn)先出(FIFO)原則,等待隊(duì)列分為Serial Dispatch Queue (同步等待隊(duì)列) 和 Concurrent Dispatch Queue (異步等待隊(duì)列)。

表 2.1-1 Dispatch Queue種類(lèi)

Dispatch Queue 說(shuō)明
Serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束
Concurrent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束

示例:

dispatch_async(queue, blk0);
dispatch_async(queue, blk1);
dispatch_async(queue, blk2);
dispatch_async(queue, blk3);
dispatch_async(queue, blk4);
dispatch_async(queue, blk5);
dispatch_async(queue, blk6);
dispatch_async(queue, blk7);
  • 當(dāng) queue 是 Serial Dispatch Queue 時(shí),執(zhí)行順序是:blk0->blk1->blk2->blk3->blk4->blk5->blk6->blk7
  • 當(dāng) queue 是 Concurrent Distach Queue 時(shí),執(zhí)行順序如下表
    表2.1-2 Concurrent Dispatch Queue 執(zhí)行示例
線(xiàn)程0 線(xiàn)程1 線(xiàn)程2 線(xiàn)程3
blk0 blk1 blk2 blk3
blk4 blk6 blk5
blk7 .

由上可以發(fā)現(xiàn)

  • GCD會(huì)為同步等待隊(duì)列開(kāi)辟一條線(xiàn)程,同步等待隊(duì)列里地任務(wù)是按照FIFO的原則在該條線(xiàn)程中逐個(gè)處理;
  • GCD會(huì)為異步等待隊(duì)列開(kāi)辟多條線(xiàn)程,異步等待隊(duì)列里的任務(wù)是會(huì)并行的在多個(gè)線(xiàn)程中進(jìn)行處理。
    本例中,GCD為異步等待隊(duì)列開(kāi)辟了四條線(xiàn)程,所以一次能并發(fā)的執(zhí)行blk0-blk3四條任務(wù),剩余任務(wù)會(huì)遵循FIFO原則,等待前面的任務(wù)執(zhí)行完畢,有空閑線(xiàn)程,再利用該空閑線(xiàn)程執(zhí)行。具體的可創(chuàng)建線(xiàn)程數(shù)和最大并發(fā)線(xiàn)程數(shù)有關(guān),屬于系統(tǒng)管理。

應(yīng)用場(chǎng)景:

  • 同步等待隊(duì)列serial dispatch queue適用于需要按順序執(zhí)行的任務(wù);
  • 異步等待隊(duì)列Concurrent dispatch queue適用于不需要按順序執(zhí)行,執(zhí)行效率高的場(chǎng)景;
2.2 創(chuàng)建Dispatch Queue

獲取隊(duì)列有兩種方式,手動(dòng)創(chuàng)建和直接獲取
手動(dòng)創(chuàng)建
使用 dispatch_queue_create 函數(shù)生成

//創(chuàng)建同步等待隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建異步等待隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_CONCURRENT);

函數(shù)中第一個(gè)參數(shù)使用反向域名的方式標(biāo)記隊(duì)列,可以設(shè)為NULL,為了獲取更多調(diào)試信息建議設(shè)置;第二個(gè)參數(shù)選擇使用serial或Concurrent隊(duì)列。

IMPORTANT
If your app is built with a deployment target of OS X v10.8 and later or iOS v6.0 and later, dispatch queues are typically managed by ARC, so you do not need to retain or release the dispatch queues.
For compatibility with existing code, this behavior is configurable. See Dispatch Queues and Automatic Reference Counting for details.
翻譯:如果你的iOS app版本大于6.0,不需要手動(dòng)管理手動(dòng)創(chuàng)建的dispatch對(duì)象

?直接獲取
使用系統(tǒng)標(biāo)準(zhǔn)提供的Dispatch Queue

//主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//系統(tǒng)全局并發(fā)隊(duì)列
dispatch_queue_t golbalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

主隊(duì)列是serial dispatch queue,系統(tǒng)全局隊(duì)列是Concurrent dispatch queue,很多時(shí)候我們沒(méi)必要手動(dòng)創(chuàng)建異步隊(duì)列,直接使用系統(tǒng)的就夠了。

2.3 dispatch_set_target_queue

設(shè)置使用dispatch_queue_create函數(shù)手動(dòng)創(chuàng)建生成隊(duì)列的優(yōu)先級(jí)

//創(chuàng)建一個(gè)隊(duì)列
dispatch_queue_t myqueue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建目標(biāo)隊(duì)列
dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
//myqueue 使用 targetQueue的優(yōu)先級(jí)策略
dispatch_set_target_queue(myqueue, targetQueue);

應(yīng)用場(chǎng)景:

  • 需要變更執(zhí)行優(yōu)先級(jí)的情況
  • 需要防止并行執(zhí)行的場(chǎng)景,可以將serial queue設(shè)為并發(fā)隊(duì)列的參考。
2.4 dispatch_after

定時(shí)處理

//設(shè)置定時(shí)器為3秒
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);

//3秒后提交任務(wù)到主隊(duì)列
dispatch_after(time, dispatch_get_main_queue(), ^{
    
    //操作任務(wù)
});

注意:3秒后提交不等于3秒后執(zhí)行,具體執(zhí)行時(shí)間和當(dāng)前正在處理任務(wù)數(shù)以及RunLoop周期有關(guān)

應(yīng)用場(chǎng)景:

  • 需要延時(shí)提交的情況
2.5 Dispatch Group

分組管理任務(wù),能監(jiān)測(cè)整個(gè)分組中的任務(wù)執(zhí)行狀態(tài)。

//獲取隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//創(chuàng)建分組
dispatch_group_t group = dispatch_group_create();

//異步提交block任務(wù)到等待隊(duì)列并分組
dispatch_group_async(group, queue, ^{NSLog(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});

/*當(dāng)上面提交到group中的block全部執(zhí)行完畢,將完成操作的block提交到queue中等待執(zhí)行*/
dispatch_group_notify(group, queue, ^{
    //完成操作
    NSLog(@"完成");
});

執(zhí)行結(jié)果:
blk1 -> blk2 -> blk0 ->完成

因?yàn)閝ueue是Concurrent queue 所以多個(gè)線(xiàn)程并行執(zhí)行,執(zhí)行完畢后再執(zhí)行"完成"

這里還有一個(gè)函數(shù)值得注意,dispatch_group_wait函數(shù),該函數(shù)也可以等待group分組任務(wù)完成后做一些特殊操作,會(huì)在指定時(shí)間內(nèi)等待分組任務(wù)執(zhí)行完畢,然后進(jìn)入下一行代碼;如果指定時(shí)間內(nèi)分組內(nèi)任務(wù)未執(zhí)行完畢,該函數(shù)返回1,否則返回0.

//一直等待直到完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

//等待3秒時(shí)間
long result = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC));
if (result == 0) {
    
    /*
     * group分組內(nèi)的任務(wù)全都執(zhí)行完畢
     */
} else {

    /*
     * group分組內(nèi)某個(gè)任務(wù)還在執(zhí)行中
     */
}

應(yīng)用場(chǎng)景:

  • 在提交到隊(duì)列中的所有任務(wù)執(zhí)行完畢后做結(jié)束處理
  • 提交任務(wù)到主隊(duì)列,將主隊(duì)列和一個(gè)group關(guān)聯(lián),可以監(jiān)測(cè)任務(wù)是否都執(zhí)行完畢,不耗費(fèi)多余等待時(shí)間。

dispatch_group_enter
指示一個(gè)block任務(wù)已經(jīng)進(jìn)入group狀態(tài)
dispatch_group_leave
指示一個(gè)block任務(wù)執(zhí)行完畢,可以退出組管理
以上兩個(gè)方法,類(lèi)似于任務(wù)計(jì)數(shù)器,協(xié)同group管理自己的任務(wù),當(dāng)沒(méi)有任務(wù),即任務(wù)計(jì)數(shù)為0時(shí)調(diào)用dispatch_group_notify函數(shù)

2.6 dispatch_barrier_async

這里申明一個(gè)概念**dispatch_async **和 dispatch_sync區(qū)別

  • dispatch_sync:提交任務(wù)到隊(duì)列,并等待block中任務(wù)執(zhí)行完畢進(jìn)入下一步
  • dispatch_async:提交任務(wù)到隊(duì)列,不等待block中任務(wù)執(zhí)行完畢即可進(jìn)入下一步。
dispatch_async(queue, ^{NSLog(@"blk0_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk1_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk2_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk3_for_reading");});

dispatch_barrier_async(queue, ^{
    //blk4 writing任務(wù)
   //添加blk4到隊(duì)列中,blk4前面提交的block任務(wù)執(zhí)行完畢后,blk4后面提教的block任務(wù)才能執(zhí)行
});
dispatch_barrier_sync(queue, ^{
    //blk4 writing任務(wù)
    
    /*如果使用這個(gè)函數(shù)
     添加blk4到隊(duì)列中,并等待blk4前面的任務(wù)執(zhí)行完畢,執(zhí)行blk4,然后進(jìn)入下一步繼續(xù)提交任務(wù)。
     
     */
    
});

dispatch_async(queue, ^{NSLog(@"blk5_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk6_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk7_for_reading");});

上面的代碼中,dispatch_barrier_async 和 dispatch_barrier_sync都起到了隔離blk0-blk3 和 blk5-blk6執(zhí)行順序功能,不同的是dispatch_barrier_async這一步代碼不會(huì)阻塞,會(huì)繼續(xù)向下執(zhí)行提交操作,dispatch_barrier_sync會(huì)阻塞代碼,停止提交,等待執(zhí)行完畢載提交。

2.7 dispatch_apply

該函數(shù)按指定的次數(shù)將指定的Block提交到指定的dispatch queue中,并等待全部執(zhí)行完畢。當(dāng)queue是Concurrent dispatch queue時(shí),所有提交的block可以異步的執(zhí)行,但是要注意安全操作問(wèn)題。

dispatch_apply(10, queue, ^(size_t index) {
   
    NSLog(@"%zu", index);
    
});

可以放入一個(gè)全局隊(duì)列,異步執(zhí)行。

2.8 dispatch_suspend/dispatch_resume

暫停隊(duì)列 和 恢復(fù)隊(duì)列

dispatch_suspend(queue);//暫停
dispatch_resume(queue);//恢復(fù)
2.9 dispatch semaphore

出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)的時(shí)候,可以進(jìn)行排他控制,使用信號(hào)機(jī)制,0代表等待返回,阻塞代碼,直到信號(hào)>=1
生成計(jì)數(shù)信號(hào)
dispatch_semaphore_t semphore = dispatch_semphore_create(1);

使用wait函數(shù)判斷計(jì)數(shù)信號(hào)數(shù)值是否>=1,是則-1并返回;否則阻塞代碼等待,直到某處將信號(hào)值設(shè)置為>=1.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

將計(jì)數(shù)值+1
dispatch_semaphore_signal(semaphore)

應(yīng)用場(chǎng)景:

  • 資源競(jìng)爭(zhēng),控制先后順序
3.0 dispatch_once

保證某個(gè)操作只執(zhí)行一次

static dispatch_once_t pred;
dispatch_once(&pred,^{
//do something
  
});
3.1 dispatch I/O

使用多個(gè)線(xiàn)程將數(shù)據(jù)分段更快的讀取數(shù)據(jù)。

現(xiàn)在我們回到一開(kāi)始提出三個(gè)使用多線(xiàn)程的問(wèn)題,資源競(jìng)爭(zhēng)、死鎖、內(nèi)存消耗

參考:
GCD官方文檔
《Objective-c 高級(jí)編程》

最后編輯于
?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • iOS中GCD的使用小結(jié) 作者dullgrass 2015.11.20 09:41*字?jǐn)?shù) 4996閱讀 20199...
    DanDanC閱讀 1,300評(píng)論 0 0
  • 本篇博客共分以下幾個(gè)模塊來(lái)介紹GCD的相關(guān)內(nèi)容: 多線(xiàn)程相關(guān)概念 多線(xiàn)程編程技術(shù)的優(yōu)缺點(diǎn)比較? GCD中的三種隊(duì)列...
    有夢(mèng)想的老伯伯閱讀 1,090評(píng)論 0 4
  • 1. GCD簡(jiǎn)介 什么是GCD呢?我們先來(lái)看看百度百科的解釋簡(jiǎn)單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 506評(píng)論 0 0
  • 標(biāo)簽(空格分隔): java 方法一:split() 方法——JDK 1.4 or later## 例子: 分隔符...
    背影殺手不太冷閱讀 13,247評(píng)論 1 3
  • 滿(mǎn)街都是新鞋, 我是多么寒傖。 纏著媽媽一路哭鬧 直到突然看見(jiàn), 一位失腳的病人。
    云中守望閱讀 256評(píng)論 0 4

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