多線程Grand Central Dispatch----GCD(1)

最近比較清閑,寫(xiě)一下多線程GCD野哭。

多線程如果使用不當(dāng)在塔,很容易引起死鎖、數(shù)據(jù)競(jìng)爭(zhēng)拨黔、因線程太多消耗過(guò)多的內(nèi)存和因增加了cpu處理壓力造成電量消耗過(guò)快等問(wèn)題蛔溃,雖然要回避這些問(wèn)題有許多方法,但是很多都偏于復(fù)雜篱蝇,盡管極易發(fā)生很多問(wèn)題城榛,很多時(shí)候也需要使用多線程開(kāi)發(fā),因?yàn)槎嗑€程可以保證應(yīng)用的響應(yīng)性能态兴,例如:執(zhí)行時(shí)間較長(zhǎng)的處理仍然可以保證用戶的界面響應(yīng)狠持。

我們先說(shuō)GCD的隊(duì)列(Queue),隊(duì)列分為并發(fā)隊(duì)列(Concurrent)和串型隊(duì)列(Serial)瞻润。

Dispatch Queue 隊(duì)列

DISPATCH_QUEUE_SERIAL

在Serial隊(duì)列里面喘垂,使用的是一個(gè)線程,順序執(zhí)行你的操作绍撞。但是可以同時(shí)創(chuàng)建多個(gè)Serial隊(duì)列正勒,讓多個(gè)Serial同時(shí)執(zhí)行。但是我們盡量不要使用DISPATCH_QUEUE_SERIAL生成多個(gè)線程傻铣,因?yàn)檫@樣容易引起數(shù)據(jù)競(jìng)爭(zhēng)等問(wèn)題章贞。

DISPATCH_QUEUE_CONCURRENT

在Concurrent隊(duì)列里面,使用的是多個(gè)線程非洲,執(zhí)行的你的代碼塊的時(shí)候鸭限,A代碼塊執(zhí)行,不管你A塊又沒(méi)有執(zhí)行完畢两踏,B塊都開(kāi)始執(zhí)行败京,也就是使用多個(gè)線程同時(shí)執(zhí)行多個(gè)處理。

dispatch_queue_create

通過(guò)dispatch_queue_create可以創(chuàng)建線程隊(duì)列梦染,例如:
dispatch_queue_t queue = dispatch_queue_create("Concurrent.Queue", DISPATCH_QUEUE_CONCURRENT);
這個(gè)創(chuàng)建的線程隊(duì)列對(duì)象名字叫 queue赡麦,是一個(gè)并發(fā)隊(duì)列朴皆,如果將他加入一段代碼,執(zhí)行的順序是這樣的

dispatch_queue_t queue = dispatch_queue_create("Concurrent.Queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        /*
         執(zhí)行耗時(shí)操作*/
        for (NSInteger i = 0; i < 10000; i++) {
            NSLog(@"%ld", (long)i);
        }
    });
    /*
     響應(yīng)其他處理*/
    NSLog(@"在隊(duì)列queue未執(zhí)行完畢時(shí)候泛粹,就已經(jīng)開(kāi)始執(zhí)行這個(gè)打印");

上述代碼打印的結(jié)果是:


圖1.jpg

Main Dispatch Queue

Main Dispatch Queue是主線程中執(zhí)行的線程隊(duì)列遂铡,其獲取方法是系統(tǒng)已經(jīng)預(yù)置好的dispatch_get_main_queue()。
這個(gè)線程的主要用于,當(dāng)開(kāi)啟多個(gè)線程處理耗時(shí)操作晶姊,當(dāng)其中一個(gè)耗時(shí)操作處理完畢需要刷新UI時(shí)扒接,就需要在dispatch_get_main_queue()中刷新,因?yàn)閁I只能在主線程中刷新帽借。
獲取方法:
dispatch_queue_t main = dispatch_get_main_queue();

Global Dispatch Queue

Global Dispatch Queue是Concurrent類型線程隊(duì)列珠增,Global Dispatch Queue 有4個(gè)優(yōu)先級(jí)超歌,如下:

#define DISPATCH_QUEUE_PRIORITY_HIGH 2    /*高優(yōu)先級(jí)*/
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 /*默認(rèn)優(yōu)先級(jí)*/
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)  /*低優(yōu)先級(jí)*/
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND  INT16_MIN /*后臺(tái)優(yōu)先級(jí)*/

雖然Global有4個(gè)優(yōu)先級(jí)砍艾,但是根據(jù)以往經(jīng)驗(yàn)和查閱的資料來(lái)判斷,這4個(gè)優(yōu)先級(jí)并不能保證實(shí)時(shí)性巍举,只能作為大致脆荷、大概的判斷,比如這個(gè)處理的內(nèi)容要求盡可能的最先處理完畢懊悯,那么我們就使用DISPATCH_QUEUE_PRIORITY_HIGH蜓谋,如果處理的內(nèi)容可有可無(wú),則可以用DISPATCH_QUEUE_PRIORITY_BACKGROUND炭分。
示例如下:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /*
         執(zhí)行耗時(shí)操作*/
        for (NSInteger i = 0; i < 10000; i++) {
            NSLog(@"%ld", (long)i);
        }
    
        /*
         進(jìn)入主線程桃焕,例如刷新UI*/
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"可以執(zhí)行主線程操作");
        });
    });

打印結(jié)果如圖2


圖2.jpg

dispatch_after

dispatch_after可以執(zhí)行指定時(shí)間的操作,在多少時(shí)間以后執(zhí)行操作捧毛,所以他有以下四個(gè)時(shí)間單位

SEC:秒
MSEC:毫秒
USEC:微秒
NSEC:納秒
PER:每

#define NSEC_PER_SEC 1000000000ull  /*每秒有多少納秒观堂,使用的時(shí)候,這個(gè)就是最常用的秒*/
#define NSEC_PER_MSEC 1000000ull    /*每毫秒有多少納秒*/
#define USEC_PER_SEC 1000000ull     /*每秒有多少微秒呀忧。(注意是指在納秒的基礎(chǔ)上)*/
#define NSEC_PER_USEC 1000ull       /*每微秒有多少納秒*/

獲取時(shí)間的節(jié)點(diǎn)有兩個(gè)

#define DISPATCH_TIME_NOW (0ull)        /*當(dāng)前*/
#define DISPATCH_TIME_FOREVER (~0ull)   /*在遙遠(yuǎn)的未來(lái)*/

值得注意的是师痕,Main Dispatch Queue是在主線程的RunLoop執(zhí)行的,所以在dispatch_after線程Block最快要在5秒執(zhí)行(假設(shè)我們?cè)O(shè)置的dispatch_time_t 是5秒)而账,但是可能因?yàn)橹骶€程因有大量的處理或者主線程本身就有以下延時(shí)操作胰坟,那么等待的時(shí)間將會(huì)更長(zhǎng)。
代碼示例:

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"hh:mm:ss:SSS"];
    
    NSDate *datenow = [NSDate date];
    NSString *nowtimeStr = [formatter stringFromDate:datenow];
    NSLog(@"now =  %@",nowtimeStr);
    
    /*延時(shí)操作*/
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSDate *dateNow2 = [NSDate date];
        NSString *nowtimeStr2 = [formatter stringFromDate:dateNow2];
        NSLog(@"now2 =  %@",nowtimeStr2);
    });

根據(jù)打印結(jié)果可以得知泞辐,延時(shí)操作并不是絕對(duì)的5秒笔横,雖然如此,但是如果只是想大致的執(zhí)行延時(shí)處理咐吼,這個(gè)函數(shù)還是可以的狠裹,打印結(jié)果如圖3。


圖3.jpg

Dispatch Group

Group線程是當(dāng)我們指定幾個(gè)線程汽烦,可以是異步的耗時(shí)操作涛菠,當(dāng)幾個(gè)耗時(shí)操作執(zhí)行完畢后再執(zhí)行。
先說(shuō)一下幾個(gè)關(guān)鍵函數(shù):

/*Group線程對(duì)象*/
dispatch_group_t;

/*生成Group線程對(duì)象的方法,使用方法和dispatch_get_main_queue()一樣*/ 
dispatch_group_create(void);

/*該函數(shù)與dispatch_async函數(shù)相同俗冻,但是dispatch_group_async有兩個(gè)參數(shù)礁叔,第一個(gè)要填寫(xiě)創(chuàng)建的dispatch_group_t類型的對(duì)象,第二個(gè)dispatch_queue_t類型的對(duì)象*/
void dispatch_group_async(dispatch_group_t group,
    dispatch_queue_t queue,
    dispatch_block_t block);

/*該函數(shù)是當(dāng)所有的dispatch_group_async函數(shù)執(zhí)行完畢后迄薄,通知到這個(gè)函數(shù)琅关,然后執(zhí)行block里面的代碼塊,需要填寫(xiě)兩個(gè)參數(shù)讥蔽,第一個(gè)填寫(xiě)dispatch_group_t類型涣易,第二個(gè)dispatch_queue_t類型的對(duì)象*/
void dispatch_group_notify(dispatch_group_t group,
    dispatch_queue_t queue,
    dispatch_block_t block);

/*dispatch_group_wait需要填寫(xiě)兩個(gè)參數(shù),第一個(gè)參數(shù)dispatch_group_t類型冶伞,第二個(gè)參數(shù)參考dispatch_after線程的時(shí)間參數(shù)新症,如果dispatch_time_t類型填寫(xiě)成DISPATCH_TIME_FOREVER,那么就會(huì)等待所有的dispatch_group_async線程執(zhí)行完畢才會(huì)執(zhí)行判斷响禽,如果填寫(xiě)DISPATCH_TIME_FOREVER不如使用dispatch_group_notify函數(shù)*/
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

以下分別是dispatch_group_notifydispatch_group_wait兩個(gè)函數(shù)的代碼示例徒爹,具體使用根據(jù)業(yè)務(wù)和需求使用。

    /*dispatch_group_notify示例*/
    /*創(chuàng)建Dispatch Group線程*/
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0 ; i < 5; i++) {
            NSLog(@"--------1--------  %@",[NSThread currentThread]);
        }
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0 ; i < 10; i++) {
            NSLog(@"--------2--------  %@",[NSThread currentThread]);
        }
    });
    /*當(dāng)1芋类,2完成后再執(zhí)行3*/
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"--------3--------  %@",[NSThread currentThread]);
        /*打印結(jié)果圖4*/
    });
   


    /*dispatch_group_wait示例*/
    /*創(chuàng)建Dispatch Group線程*/
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0 ; i < 5; i++) {
            NSLog(@"--------1--------  %@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0 ; i < 10; i++) {
            NSLog(@"--------2--------  %@", [NSThread currentThread]);
        }
    });
    /*設(shè)置時(shí)間為1秒鐘*/
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
    long result = dispatch_group_wait(group, time);
     /*在1秒鐘內(nèi)隆嗅,1和2全部執(zhí)行完畢得到long值為0*/
    /*打印結(jié)果圖5*/
    if (!result) {
        NSLog(@"dispatch_group_async全部執(zhí)行完畢  %@", [NSThread currentThread]);
    } else {
        NSLog(@"dispatch_group_async還有在執(zhí)行的,但是等到時(shí)間到了  %@", [NSThread currentThread]);
    }


    /*將循環(huán)次數(shù)增大侯繁,增加循環(huán)打印時(shí)間超過(guò)1秒的情況*/
    /*創(chuàng)建Dispatch Group線程*/
    dispatch_group_t group = dispatch_group_create();
    /*將循環(huán)次數(shù)增大*/
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0 ; i < 5000; i++) {
            NSLog(@"--------1--------  %@", [NSThread currentThread]);
        }
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0 ; i < 10000; i++) {
            NSLog(@"--------2--------  %@", [NSThread currentThread]);
        }
    });
    /*設(shè)置時(shí)間為1秒鐘*/
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
    long result = dispatch_group_wait(group, time);
     /*在1秒鐘內(nèi)胖喳,1和2沒(méi)有執(zhí)行完畢得到long值為>0*/
    /*打印結(jié)果圖6*/
    if (!result) {
        NSLog(@"dispatch_group_async全部執(zhí)行完畢  %@", [NSThread currentThread]);
    } else {
        NSLog(@"dispatch_group_async還有在執(zhí)行的,但是等到時(shí)間到了  %@", [NSThread currentThread]);
    }
圖4.jpg

圖5.jpg

圖6.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贮竟,一起剝皮案震驚了整個(gè)濱河市丽焊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坝锰,老刑警劉巖粹懒,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異顷级,居然都是意外死亡凫乖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)弓颈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)帽芽,“玉大人,你說(shuō)我怎么就攤上這事翔冀〉冀郑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵纤子,是天一觀的道長(zhǎng)搬瑰。 經(jīng)常有香客問(wèn)我款票,道長(zhǎng),這世上最難降的妖魔是什么泽论? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任艾少,我火速辦了婚禮,結(jié)果婚禮上翼悴,老公的妹妹穿的比我還像新娘缚够。我一直安慰自己,他們只是感情好鹦赎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布谍椅。 她就那樣靜靜地躺著,像睡著了一般古话。 火紅的嫁衣襯著肌膚如雪雏吭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,573評(píng)論 1 305
  • 那天煞额,我揣著相機(jī)與錄音思恐,去河邊找鬼沾谜。 笑死膊毁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的基跑。 我是一名探鬼主播婚温,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼媳否!你這毒婦竟也來(lái)了栅螟?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤篱竭,失蹤者是張志新(化名)和其女友劉穎力图,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體掺逼,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吃媒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吕喘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赘那。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖氯质,靈堂內(nèi)的尸體忽然破棺而出募舟,到底是詐尸還是另有隱情,我是刑警寧澤闻察,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布拱礁,位于F島的核電站琢锋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏呢灶。R本人自食惡果不足惜吩蔑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望填抬。 院中可真熱鬧烛芬,春花似錦、人聲如沸飒责。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宏蛉。三九已至遣臼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拾并,已是汗流浹背揍堰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗅义,地道東北人屏歹。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像之碗,于是被迫代替她去往敵國(guó)和親蝙眶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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