iOS -- 多線程與GCD的應(yīng)用

本篇博客共分以下幾個(gè)模塊來(lái)介紹GCD的相關(guān)內(nèi)容:

  • 多線程相關(guān)概念
  • 多線程編程技術(shù)的優(yōu)缺點(diǎn)比較?
  • GCD中的三種隊(duì)列類(lèi)型
  • The main queue(主線程串行隊(duì)列)
  • Global queue(全局并發(fā)隊(duì)列)
  • Custom queue (自定義隊(duì)列)
  • Group queue (隊(duì)列組)
  • GCD中一些系統(tǒng)提供的常用dispatch方法

多線程相關(guān)概念

進(jìn)程與線程
  • 進(jìn)程概念: 進(jìn)程是程序在計(jì)算機(jī)上的一次執(zhí)行活動(dòng)诵竭,打開(kāi)一個(gè)app,就開(kāi)啟了一個(gè)進(jìn)程踩窖,可包含多個(gè)線程势就。
  • 線程概念: 獨(dú)立執(zhí)行的代碼段杀饵,一個(gè)線程同時(shí)間只能執(zhí)行一個(gè)任務(wù),反之多線程并發(fā)就可以在同一時(shí)間執(zhí)行多個(gè)任務(wù)咙鞍。
  • iOS程序中房官,主線程(又叫做UI線程)主要任務(wù)是處理UI事件,顯示和刷新UI(只有主線程有直接修改UI的能力)耗時(shí)的操作放在子線程(又叫做后臺(tái)線程续滋、異步線程)翰守。在iOS中開(kāi)子線程去處理耗時(shí)的操作,可以有效提高程序的執(zhí)行效率疲酌,提高資源利用率蜡峰。但是開(kāi)啟線程會(huì)占用一定的內(nèi)存(主線程的堆棧大小是1M,第二個(gè)線程開(kāi)始都是512KB朗恳,并且該值不能通過(guò)編譯器開(kāi)關(guān)或線程API函數(shù)來(lái)更改)降低程序的性能湿颅。所以一般不要同時(shí)開(kāi)很多線程。
線程相關(guān)
  • 同步線程:同步線程會(huì)阻塞當(dāng)前線程去執(zhí)行線程內(nèi)的任務(wù)粥诫,執(zhí)行完之后才會(huì)返回當(dāng)前線程
  • 異步線程:異步線程不會(huì)阻塞當(dāng)前線程油航,會(huì)開(kāi)啟其他線程內(nèi)的任務(wù)。
  • 串行隊(duì)列:線程任務(wù)按先后順序逐個(gè)執(zhí)行(需要等待隊(duì)列里面前面的任務(wù)執(zhí)行完之后在執(zhí)行新的任務(wù))怀浆。
  • 并發(fā)隊(duì)列:多個(gè)任務(wù)添加順序一起開(kāi)始執(zhí)行(不用等待前面的任務(wù)執(zhí)行完再執(zhí)行新的任務(wù))但是添加間隔往往忽略不計(jì)谊囚,所以看著像是一起執(zhí)行的怕享。
  • 并發(fā)VS并行:并行是基于多核設(shè)備的,并行一定是并發(fā)秒啦,并發(fā)不一定是并行。
多線程中會(huì)出現(xiàn)的問(wèn)題
  • Critical Section(臨界代碼段)
    指的是不能同時(shí)被兩個(gè)線程訪問(wèn)的代碼段搀玖,比如一個(gè)變量余境,被并發(fā)進(jìn)程訪問(wèn)后可能會(huì)改變變量值,造成數(shù)據(jù)污染(數(shù)據(jù)共享問(wèn)題)灌诅。

  • Race Condition (競(jìng)態(tài)條件)
    當(dāng)多個(gè)線程同時(shí)訪問(wèn)共享的數(shù)據(jù)時(shí)芳来,會(huì)發(fā)生爭(zhēng)用情形,第一個(gè)線程讀取改變了一個(gè)變量的值猜拾,第二個(gè)線程也讀取改變了這個(gè)變量的值即舌,兩個(gè)線程同時(shí)操作了該變量,此時(shí)他們會(huì)發(fā)生競(jìng)爭(zhēng)來(lái)看哪個(gè)線程會(huì)最后寫(xiě)入這個(gè)變量挎袜,最后被寫(xiě)入的值將會(huì)被保留下來(lái)顽聂。

  • Deadlock (死鎖)
    兩個(gè)(多個(gè))線程都要等待對(duì)方完成某個(gè)操作才能進(jìn)行下一步,這時(shí)就會(huì)發(fā)生死鎖盯仪。

  • Thread Safe(線程安全)
    一段線程安全的代碼(對(duì)象)紊搪,可以同時(shí)被多個(gè)線程或并發(fā)的任務(wù)調(diào)度,不會(huì)產(chǎn)生問(wèn)題全景,非線程安全的只能按次序被訪問(wèn)耀石。
    所有Mutable對(duì)象都是非線程安全的,所有Immutable對(duì)象都是線程安全的爸黄,使用Mutable對(duì)象滞伟,一定要用同步鎖來(lái)同步訪問(wèn)(@synchronized)。

  • 互斥鎖:能夠防止多線程搶奪造成的數(shù)據(jù)安全問(wèn)題炕贵,但是需要消耗大量的資源

  • 原子屬性(atomic)加鎖

    • atomic: 原子屬性梆奈,為setter方法加鎖,將屬性以atomic的形式來(lái)聲明称开,該屬性變量就能支持互斥鎖了鉴裹。
  • nonatomic: 非原子屬性,不會(huì)為setter方法加鎖钥弯,聲明為該屬性的變量径荔,客戶(hù)端應(yīng)盡量避免多線程爭(zhēng)奪同一資源。

  • Context Switch (上下文切換)
    當(dāng)一個(gè)進(jìn)程中有多個(gè)線程來(lái)回切換時(shí)脆霎,context switch用來(lái)記錄執(zhí)行狀態(tài)总处,這樣的進(jìn)程和一般的多線程進(jìn)程沒(méi)有太大差別,但會(huì)產(chǎn)生一些額外的開(kāi)銷(xiāo)睛蛛。

多線程編程技術(shù)的優(yōu)缺點(diǎn)比較

  • NSThread(抽象層次:低)
  • 優(yōu)點(diǎn):輕量級(jí)鹦马,簡(jiǎn)單易用胧谈,可以直接操作線程對(duì)象
  • 缺點(diǎn):需要自己管理線程的生命周期,線程同步荸频。線程同步對(duì)數(shù)據(jù)的加鎖會(huì)有一定的系統(tǒng)開(kāi)銷(xiāo)
  • Cocoa NSOperation (抽象層次:中)
  • 優(yōu)點(diǎn):不需要關(guān)心線程管理菱肖,數(shù)據(jù)同步的事情,可以把精力放在學(xué)要執(zhí)行的操作上旭从∥惹浚基于GCD,是對(duì)GCD 的封裝和悦,比GCD更加面向?qū)ο?/li>
  • 缺點(diǎn): NSOperation是個(gè)抽象類(lèi)退疫,使用它必須使用它的子類(lèi),可以實(shí)現(xiàn)它或者使用它定義好的兩個(gè)子類(lèi)NSInvocationOperation鸽素、NSBlockOperation.
  • GCD 全稱(chēng)Grand Center Dispatch (抽象層次:高)
GCD的抽象層次最高褒繁,使用也簡(jiǎn)單,因此馍忽,蘋(píng)果也推薦使用GCD
  • 優(yōu)點(diǎn):是 Apple 開(kāi)發(fā)的一個(gè)多核編程的解決方法棒坏,簡(jiǎn)單易用,效率高遭笋,速度快俊抵,基于C語(yǔ)言,更底層更高效坐梯,并且不是Cocoa框架的一部分徽诲,自動(dòng)管理線程生命周期(創(chuàng)建線程、調(diào)度任務(wù)吵血、銷(xiāo)毀線程)谎替。
  • 缺點(diǎn): 使用GCD的場(chǎng)景如果很復(fù)雜,就有非常大的可能遇到死鎖問(wèn)題蹋辅。

GCD中的三種隊(duì)列類(lèi)型

GCD編程的核心就是dispatch隊(duì)列钱贯,dispatch block的執(zhí)行最終會(huì)放進(jìn)某個(gè)隊(duì)列中去進(jìn)行。
  • the main queue(主線程串行隊(duì)列): 與主線程功能相同侦另,提交至 Main queue的任務(wù)會(huì)在主線程中執(zhí)行秩命,
  • Main queue 可以通過(guò)dispatch_get_main_queue()來(lái)獲取。
  • Global queue(全局并發(fā)隊(duì)列): 全局并發(fā)隊(duì)列由整個(gè)進(jìn)程共享褒傅,有高弃锐、中(默認(rèn))、低殿托、后臺(tái)四個(gè)優(yōu)先級(jí)別霹菊。
  • Global queue 可以通過(guò)調(diào)用dispatch_get_global_queue函數(shù)來(lái)獲取(可以設(shè)置優(yōu)先級(jí))
  • Custom queue (自定義隊(duì)列): 可以為串行支竹,也可以為并發(fā)旋廷。
  • Custom queue 可以通過(guò)dispatch_queue_create()來(lái)獲瑞础;
  • Group queue (隊(duì)列組):將多線程進(jìn)行分組饶碘,最大的好處是可獲知所有線程的完成情況目尖。
  • Group queue 可以通過(guò)調(diào)用dispatch_group_create()來(lái)獲取,通過(guò) - dispatch_group_notify,可以直接監(jiān)聽(tīng)組里所有線程完成情況扎运。

The main queue(主線程串行隊(duì)列)

dispatch_sync 同步執(zhí)行任務(wù)函數(shù)瑟曲,不會(huì)開(kāi)啟新的線程,dispatch_async 異步執(zhí)行任務(wù)函數(shù)绪囱,會(huì)開(kāi)啟新的線程

  • 獲取主線程串行隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
  • 主線程串行隊(duì)列同步執(zhí)行任務(wù)测蹲,在主線程運(yùn)行時(shí)莹捡,會(huì)產(chǎn)生死鎖
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue,^{
NSLog("MainQueue"); 
});

程序正常運(yùn)行鬼吵,block中的代碼正常運(yùn)行

  • 從子線程,異步返回主線程更新UI<這種使用方式比較多>
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
    //子線程異步執(zhí)行下載任務(wù)篮赢,防止主線程卡頓
    NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
    NSError *error;
    NSString *htmlData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
    if (htmlData != nil) {
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        //異步返回主線程齿椅,根據(jù)獲取的數(shù)據(jù),更新UI
        dispatch_async(mainQueue, ^{
            NSLog(@"根據(jù)更新UI界面");
        });
    } else {
        NSLog(@"error when download:%@",error);
    }
});

主線程串行隊(duì)列由系統(tǒng)默認(rèn)生成的启泣,所以無(wú)法調(diào)用dispatch_resume()和dispatch_suspend()來(lái)控制執(zhí)行繼續(xù)或中斷涣脚。

Global queue(全局并發(fā)隊(duì)列)

耗時(shí)的操作,比如讀取網(wǎng)絡(luò)數(shù)據(jù)寥茫,IO,數(shù)據(jù)庫(kù)讀寫(xiě)等遣蚀,我們會(huì)在另外一個(gè)線程中處理這些操作,然后通知主線程更新界面

  • 獲取全局并發(fā)隊(duì)列
//程序默認(rèn)的隊(duì)列級(jí)別纱耻,一般不要修改,DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
dispatch_queue_t globalQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//HIGH
dispatch_queue_t globalQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//LOW
dispatch_queue_t globalQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//BACKGROUND
dispatch_queue_t globalQueue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
  • 全局并發(fā)隊(duì)列同步執(zhí)行任務(wù)芭梯,在主線程執(zhí)行會(huì)導(dǎo)致頁(yè)面卡頓。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_sync(globalQueue, ^{
    sleep(2.0);
    NSLog(@"sleep 2.0s");
});
NSLog(@"next task");

控制臺(tái)輸出如下:

2015-11-18 15:50:14.999 Whisper[33073:343781] current task
2015-11-18 15:50:15.000 Whisper[33073:343781] next task
2015-11-18 15:50:17.004 Whisper[33073:343841] sleep 2.0s

主線程不用等待2s鐘弄喘,繼續(xù)執(zhí)行block代碼段后面的代碼玖喘。

  • 多個(gè)全局并發(fā)隊(duì)列,異步執(zhí)行任務(wù)蘑志。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_async(globalQueue, ^{
    NSLog(@"最先加入全局并發(fā)隊(duì)列");
});
dispatch_async(globalQueue, ^{
    NSLog(@"次加入全局并發(fā)隊(duì)列");
});
NSLog(@"next task");

控制臺(tái)輸出如下:

2015-11-18 16:54:52.202 Whisper[39827:403208] current task
2015-11-18 16:54:52.203 Whisper[39827:403208] next task
2015-11-18 16:54:52.205 Whisper[39827:403309] 最先加入全局并發(fā)隊(duì)列
2015-11-18 16:54:52.205 Whisper[39827:403291] 次加入全局并發(fā)隊(duì)列

異步線程的執(zhí)行順序是不確定的累奈。幾乎同步開(kāi)始執(zhí)行
全局并發(fā)隊(duì)列由系統(tǒng)默認(rèn)生成的,所以無(wú)法調(diào)用dispatch_resume()和dispatch_suspend()來(lái)控制執(zhí)行繼續(xù)或中斷急但。

Custom queue (自定義隊(duì)列)

自定義串行隊(duì)列

  • 獲取自定義串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"%s",dispatch_queue_get_label(conCurrentQueue)) ;
  • 控制臺(tái)輸出:
2015-11-19 11:05:34.469 Whisper[1223:42960] com.dullgrass.serialQueue

dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)函數(shù)中第一個(gè)參數(shù)是給這個(gè)queue起的標(biāo)識(shí)澎媒,這個(gè)在調(diào)試的可以看到是哪個(gè)隊(duì)列在執(zhí)行,或者在crash日志中波桩,也能做為提示旱幼。第二個(gè)是需要?jiǎng)?chuàng)建的隊(duì)列類(lèi)型,是串行的還是并發(fā)的

  • 自定義串行隊(duì)列同步執(zhí)行任務(wù)
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"current task");
dispatch_sync(serialQueue, ^{
    NSLog(@"最先加入自定義串行隊(duì)列");
    sleep(2);
});
dispatch_sync(serialQueue, ^{
    NSLog(@"次加入自定義串行隊(duì)列");
});
NSLog(@"next task");
  • 控制臺(tái)輸出:
2015-11-18 17:09:40.025 Whisper[40241:416296] current task
2015-11-18 17:09:40.027 Whisper[40241:416296] 最先加入自定義串行隊(duì)列
2015-11-18 17:09:43.027 Whisper[40241:416296] 次加入自定義串行隊(duì)列
2015-11-18 17:09:43.027 Whisper[40241:416296] next task

當(dāng)前線程等待串行隊(duì)列中的子線程執(zhí)行完成之后再執(zhí)行突委,串行隊(duì)列中先進(jìn)來(lái)的子線程先執(zhí)行任務(wù)柏卤,執(zhí)行完成后冬三,再執(zhí)行隊(duì)列中后面的任務(wù)。

  • 自定義串行隊(duì)列嵌套執(zhí)行同步任務(wù)缘缚,產(chǎn)生死鎖
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{   //該代碼段后面的代碼都不會(huì)執(zhí)行勾笆,程序被鎖定在這里
       NSLog(@"會(huì)執(zhí)行的代碼");
       dispatch_sync(serialQueue, ^{
           NSLog(@"代碼不執(zhí)行");
       });
});
  • 異步執(zhí)行串行隊(duì)列,嵌套同步執(zhí)行串行隊(duì)列桥滨,同步執(zhí)行的串行隊(duì)列中的任務(wù)將不會(huì)被執(zhí)行窝爪,其他程序正常執(zhí)行
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
        NSLog(@"會(huì)執(zhí)行的代碼");
        dispatch_sync(serialQueue, ^{
            NSLog(@"代碼不執(zhí)行");
        });
});

注意不要嵌套使用同步執(zhí)行的串行隊(duì)列任務(wù)

自定義并發(fā)隊(duì)列

  • 獲取自定義并發(fā)隊(duì)列
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
  • 自定義并發(fā)隊(duì)列執(zhí)行同步任務(wù)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"current task");
dispatch_sync(conCurrentQueue, ^{
    NSLog(@"先加入隊(duì)列");
});
dispatch_sync(conCurrentQueue, ^{
    NSLog(@"次加入隊(duì)列");
});
NSLog(@"next task");
  • 控制臺(tái)輸出如下:
2015-11-19 10:36:23.259 Whisper[827:20596] current task
2015-11-19 10:36:23.261 Whisper[827:20596] 先加入隊(duì)列
2015-11-19 10:36:23.261 Whisper[827:20596] 次加入隊(duì)列
2015-11-19 10:36:23.261 Whisper[827:20596] next task
  • 自定義并發(fā)隊(duì)列嵌套執(zhí)行同步任務(wù)(不會(huì)產(chǎn)生死鎖,程序正常運(yùn)行)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"current task");
dispatch_sync(conCurrentQueue, ^{
    NSLog(@"先加入隊(duì)列");
    dispatch_sync(conCurrentQueue, ^{
        NSLog(@"次加入隊(duì)列");
    });
});
NSLog(@"next task");
  • 控制臺(tái)輸出如下:
2015-11-19 10:39:21.301 Whisper[898:22273] current task
2015-11-19 10:39:21.303 Whisper[898:22273] 先加入隊(duì)列
2015-11-19 10:39:21.303 Whisper[898:22273] 次加入隊(duì)列
2015-11-19 10:39:21.303 Whisper[898:22273] next task
  • 自定義并發(fā)隊(duì)列執(zhí)行異步任務(wù)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"current task");
dispatch_async(conCurrentQueue, ^{
    NSLog(@"先加入隊(duì)列");
});
dispatch_async(conCurrentQueue, ^{
    NSLog(@"次加入隊(duì)列");
});
NSLog(@"next task");
  • 控制臺(tái)輸出如下:
2015-11-19 10:45:22.290 Whisper[1050:26445] current task
2015-11-19 10:45:22.290 Whisper[1050:26445] next task
2015-11-19 10:45:22.290 Whisper[1050:26505] 次加入隊(duì)列
2015-11-19 10:45:22.290 Whisper[1050:26500] 先加入隊(duì)列

異步執(zhí)行任務(wù)齐媒,開(kāi)啟新的子線程蒲每,不影響當(dāng)前線程任務(wù)的執(zhí)行,并發(fā)隊(duì)列中的任務(wù)喻括,幾乎是同步執(zhí)行的邀杏,輸出順序不確定

Group queue (隊(duì)列組)

當(dāng)遇到需要執(zhí)行多個(gè)線程并發(fā)執(zhí)行,然后等多個(gè)線程都結(jié)束之后唬血,再匯總執(zhí)行結(jié)果時(shí)可以用group queue

  • 使用場(chǎng)景: 同時(shí)下載多個(gè)圖片望蜡,所有圖片下載完成之后去更新UI(需要回到主線程)或者去處理其他任務(wù)(可以是其他線程隊(duì)列)。
  • 原理:使用函數(shù)dispatch_group_create創(chuàng)建dispatch group,然后使用函數(shù)dispatch_group_async來(lái)將要執(zhí)行的block任務(wù)提交到一個(gè)dispatch queue拷恨。同時(shí)將他們添加到一個(gè)組脖律,等要執(zhí)行的block任務(wù)全部執(zhí)行完成之后,使用dispatch_group_notify函數(shù)接收完成時(shí)的消息腕侄。
  • 使用示例:
dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_group_t groupQueue = dispatch_group_create();
NSLog(@"current task");
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
    NSLog(@"并行任務(wù)1");
});
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
    NSLog(@"并行任務(wù)2");
});
dispatch_group_notify(groupQueue, mainQueue, ^{
    NSLog(@"groupQueue中的任務(wù) 都執(zhí)行完成,回到主線程更新UI");
});
NSLog(@"next task");
  • 控制臺(tái)輸出
2015-11-19 13:47:55.117 Whisper[1645:97116] current task
2015-11-19 13:47:55.117 Whisper[1645:97116] next task
2015-11-19 13:47:55.119 Whisper[1645:97178] 并行任務(wù)1
2015-11-19 13:47:55.119 Whisper[1645:97227] 并行任務(wù)2
2015-11-19 13:47:55.171 Whisper[1645:97116] groupQueue中的任務(wù) 都執(zhí)行完成,回到主線程更新UI
  • 在當(dāng)前線程阻塞的同步等待dispatch_group_wait
dispatch_group_t groupQueue = dispatch_group_create();
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC);
dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
    
    long isExecuteOver = dispatch_group_wait(groupQueue, delayTime);
    if (isExecuteOver) {
        NSLog(@"wait over");
    } else {
        NSLog(@"not over");
    }
    NSLog(@"并行任務(wù)1");
});
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
    NSLog(@"并行任務(wù)2");
});
  • 控制臺(tái)輸出如下:
2015-11-19 14:37:29.514 Whisper[2426:126683] current task
2015-11-19 14:37:29.518 Whisper[2426:126791] 并行任務(wù)2
2015-11-19 14:37:39.515 Whisper[2426:126733] wait over
2015-11-19 14:37:39.516 Whisper[2426:126733] 并行任務(wù)1

dispatch_time(dispatch_time_t when, int64_t delta);
參數(shù)注釋?zhuān)?br> 第一個(gè)參數(shù)一般是DISPATCH_TIME_NOW小泉,表示從現(xiàn)在開(kāi)始
第二個(gè)參數(shù)是延時(shí)的具體時(shí)間
延時(shí)1秒可以寫(xiě)成如下幾種:
NSEC_PER_SEC----每秒有多少納秒
dispatch_time(DISPATCH_TIME_NOW, 1NSEC_PER_SEC);
USEC_PER_SEC----每秒有多少毫秒(注意是指在納秒的基礎(chǔ)上)
dispatch_time(DISPATCH_TIME_NOW, 1000
USEC_PER_SEC); //SEC---毫秒
NSEC_PER_USEC----每毫秒有多少納秒。
dispatch_time(DISPATCH_TIME_NOW,
USEC_PER_SEC*NSEC_PER_USEC);SEC---納秒

GCD中一些系統(tǒng)提供的常用dispatch方法

  • dispatch_after延時(shí)添加到隊(duì)列
    使用示例:
dispatch_time_t delayTime3 = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
dispatch_time_t delayTime2 = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"current task");
dispatch_after(delayTime3, mainQueue, ^{
    NSLog(@"3秒之后添加到隊(duì)列");
});
dispatch_after(delayTime2, mainQueue, ^{
    NSLog(@"2秒之后添加到隊(duì)列");
});
NSLog(@"next task");
  • 控制臺(tái)輸出如下:
2015-11-19 15:50:19.369 Whisper[2725:172593] current task
2015-11-19 15:50:19.370 Whisper[2725:172593] next task
2015-11-19 15:50:21.369 Whisper[2725:172593] 2秒之后添加到隊(duì)列
2015-11-19 15:50:22.654 Whisper[2725:172593] 3秒之后添加到隊(duì)列

dispatch_after只是延時(shí)提交block冕杠,并不是延時(shí)后立即執(zhí)行微姊,并不能做到精確控制,需要精確控制的朋友慎用哦

  • dispatch_apply在給定的隊(duì)列上多次執(zhí)行某一任務(wù)拌汇,在主線程直接調(diào)用會(huì)阻塞主線程去執(zhí)行block中的任務(wù)柒桑。

  • dispatch_apply函數(shù)的功能:把一項(xiàng)任務(wù)提交到隊(duì)列中多次執(zhí)行,隊(duì)列可以是串行也可以是并行噪舀,dispatch_apply不會(huì)立刻返回魁淳,在執(zhí)行完block中的任務(wù)后才會(huì)返回,是同步執(zhí)行的函數(shù)与倡。

  • dispatch_apply正確使用方法:為了不阻塞主線程界逛,一般把dispatch_apply放在異步隊(duì)列中調(diào)用,然后執(zhí)行完成后通知主線程

  • 使用示例:

dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
NSLog(@"current task");
dispatch_async(globalQueue, ^{
    dispatch_queue_t applyQueue = dispatch_get_global_queue(0, 0);
    //第一個(gè)參數(shù)纺座,3--block執(zhí)行的次數(shù)
    //第二個(gè)參數(shù)息拜,applyQueue--block任務(wù)提交到的隊(duì)列
    //第三個(gè)參數(shù),block--需要重復(fù)執(zhí)行的任務(wù)
    dispatch_apply(3, applyQueue, ^(size_t index) {
        NSLog(@"current index %@",@(index));
        sleep(1);
    });
    NSLog(@"dispatch_apply 執(zhí)行完成");
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
        NSLog(@"回到主線程更新UI");
    });
});
NSLog(@"next task");
  • 控制臺(tái)輸出如下:
2015-11-19 16:24:45.015 Whisper[4034:202269] current task
2015-11-19 16:24:45.016 Whisper[4034:202269] next task
2015-11-19 16:24:45.016 Whisper[4034:202347] current index 0
2015-11-19 16:24:45.016 Whisper[4034:202344] current index 1
2015-11-19 16:24:45.016 Whisper[4034:202345] current index 2
2015-11-19 16:24:46.021 Whisper[4034:202347] dispatch_apply 執(zhí)行完成
2015-11-19 16:24:46.021 Whisper[4034:202269] 回到主線程更新UI
  • 嵌套使用dispatch_apply會(huì)導(dǎo)致死鎖。

  • dispatch_once保證在app運(yùn)行期間少欺,block中的代碼只執(zhí)行一次

  • 經(jīng)典使用場(chǎng)景---單例

  • 單例對(duì)象ShareManager的定義:

ShareManager的.h文件
#import <Foundation/Foundation.h>
@interface ShareManager : NSObject
@property (nonatomic, copy) NSString *someProperty;
+ (ShareManager *)shareManager;
+ (ShareManager *)sharedManager;
@end

ShareManager的.m文件
#import "ShareManager.h"
@implementation ShareManager
static ShareManager *sharedManager = nil;
//GCD實(shí)現(xiàn)單例功能
+ (ShareManager *)shareManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[self alloc] init];
    });
    return sharedManager;
}
//在ARC下喳瓣,非GCD,實(shí)現(xiàn)單例功能
+ (ShareManager *)sharedManager
{
    @synchronized(self) {
        if (!sharedManager) {
            sharedManager = [[self alloc] init];
        }
    }
    return sharedManager;
}
- (instancetype)init{
    self = [super init];
    if (self) {
        _someProperty =@"Default Property Value";
    }
    return self;
}
@end

ShareManager的使用
#import "ShareManager.h"
在需要使用的函數(shù)中赞别,直接調(diào)用下面的方法
ShareManager *share = [ShareManager sharedManager];
NSLog(@"share is %@",share.someProperty);
  • dispatch_barrier_async 柵欄的作用
  • 功能:是在并行隊(duì)列中畏陕,等待在dispatch_barrier_async之前加入的隊(duì)列全部執(zhí)行完成之后(這些任務(wù)是并發(fā)執(zhí)行的)再執(zhí)行dispatch_barrier_async中的任務(wù),dispatch_barrier_async中的任務(wù)執(zhí)行完成之后仿滔,再去執(zhí)行在dispatch_barrier_async之后加入到隊(duì)列中的任務(wù)(這些任務(wù)是并發(fā)執(zhí)行的)惠毁。
  • 使用示例:
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(conCurrentQueue, ^{
    NSLog(@"dispatch 1");
});
dispatch_async(conCurrentQueue, ^{
    NSLog(@"dispatch 2");
});
dispatch_barrier_async(conCurrentQueue, ^{
    NSLog(@"dispatch barrier");
});
dispatch_async(conCurrentQueue, ^{
    NSLog(@"dispatch 3");
});
dispatch_async(conCurrentQueue, ^{
    NSLog(@"dispatch 4");
});
  • 控制臺(tái)輸出如下:
2015-11-19 18:12:34.125 Whisper[22633:297257] dispatch 1
2015-11-19 18:12:34.125 Whisper[22633:297258] dispatch 2
2015-11-19 18:12:34.126 Whisper[22633:297258] dispatch barrier
2015-11-19 18:12:34.127 Whisper[22633:297258] dispatch 3
2015-11-19 18:12:34.127 Whisper[22633:297257] dispatch 4
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市崎页,隨后出現(xiàn)的幾起案子鞠绰,更是在濱河造成了極大的恐慌,老刑警劉巖飒焦,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜈膨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡荒给,警方通過(guò)查閱死者的電腦和手機(jī)丈挟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)刁卜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)志电,“玉大人,你說(shuō)我怎么就攤上這事蛔趴√袅荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵孝情,是天一觀的道長(zhǎng)鱼蝉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)箫荡,這世上最難降的妖魔是什么魁亦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮羔挡,結(jié)果婚禮上洁奈,老公的妹妹穿的比我還像新娘。我一直安慰自己绞灼,他們只是感情好利术,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著低矮,像睡著了一般印叁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天轮蜕,我揣著相機(jī)與錄音昨悼,去河邊找鬼。 笑死跃洛,一個(gè)胖子當(dāng)著我的面吹牛幔戏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播税课,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼闲延,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了韩玩?” 一聲冷哼從身側(cè)響起垒玲,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎找颓,沒(méi)想到半個(gè)月后合愈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡击狮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年佛析,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彪蓬。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寸莫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出档冬,到底是詐尸還是另有隱情膘茎,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布酷誓,位于F島的核電站披坏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏盐数。R本人自食惡果不足惜棒拂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望玫氢。 院中可真熱鬧帚屉,春花似錦、人聲如沸琐旁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)灰殴。三九已至敬特,卻和暖如春掰邢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伟阔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工辣之, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人皱炉。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓怀估,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親合搅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子多搀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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