iOS中多線程GCD(Grand Center Dispatch)

ios中多線程GCD(Grand Center Dispatch)

  1. 特別注意ios中主線程又稱作為 UI線程, 主要任務(wù)就是處理UI事件, 即顯示和刷新UI,
  2. 只有主線程具有直接修改UI的能力, 那些耗時(shí)的(從網(wǎng)絡(luò)獲取數(shù)據(jù) | 加載圖片 | 數(shù)據(jù)庫讀取 | IO等)操作要放在子線程(又稱為后臺(tái)線程或者異步線程)中處理, 這樣可以提高程序執(zhí)行效率和資源利用率, 最重要的用戶的 UI 的體驗(yàn)也會(huì)很好.
  3. 死鎖: 兩個(gè)或者多個(gè)線程都要等待對(duì)方完成某個(gè)操作才能進(jìn)行下一步, 從而產(chǎn)生死鎖. 如: 在主線程串行隊(duì)列執(zhí)行同步任務(wù),會(huì)產(chǎn)生死鎖.

常見代碼示例:

dispatch_queue_t globalQueue = dispatch_get_global_queu(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(globalQueue, ^{
    //異步 加載數(shù)據(jù)
    NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
    NSError *error;
    NSString *htmlData = [NSString stringWithContentsWithURL:url encoding:NSUTF8StringEncoding error:&error];
    //加載完畢切換到 主線程中更新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"回到主隊(duì)列更新UI界面 = %@", htmlData);
    });
});

同步和異步

同步和異步?jīng)Q定了要不要開啟新的線程

  • 同步: 在當(dāng)前線程中執(zhí)行任務(wù), 不具備開啟新線程的能力.

    dispatch_sync(queue, ^{});
    
  • 異步: 在新的線程中執(zhí)行任務(wù), 具備開啟新線程的能力.

    dispatch_async(queue, ^{});
    

并發(fā)和串行

并發(fā)和串行決定了任務(wù)的執(zhí)行方式

  • 并發(fā): 多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行.

    并發(fā)隊(duì)列: dispatch_get_global_queue(0, 0);
    
  • 串行: 一個(gè)任務(wù)執(zhí)行完畢后, 再執(zhí)行下一個(gè)任務(wù).

    主隊(duì)列: dispatch_get_main_queue(); 
    

同步和異步 & 并發(fā)和串行

ios中常用的搭配組合:

  • 同步--串行(詳情看下面自定義串行隊(duì)列例子), 有特殊情況(主隊(duì)列要對(duì)應(yīng)異步, 否則會(huì)造成死鎖)

    dispatch_async(dispatch_get_main_queue(), ^{});
    
  • 異步--并行:

    dispatch_async(dispatch_get_global_queue(0, 0), ^{});
    
  • 其他情況等同于上面兩種情況, 就不多介紹了

GCD中三種隊(duì)列類型

  • 主線程串行隊(duì)列(main queue)
  • 全局并發(fā)隊(duì)列(global queue)
  • 自定義隊(duì)列(custom queue)
  • 還有一個(gè)就是基于前面的三種隊(duì)列形成的隊(duì)列組(group queue)

詳細(xì)介紹

GCD編程核心:就是dispatch隊(duì)列.

即: 將任務(wù)放到block中, dispatch分發(fā)到相對(duì)應(yīng)的隊(duì)列中去執(zhí)行.

  1. 主線程隊(duì)列(main queue)--> 串行:

    即將任務(wù)(block)放到主隊(duì)列中去, 在主線程中執(zhí)行, 注意主隊(duì)列默認(rèn)是串行的(即:若此刻主隊(duì)列正在執(zhí)行任務(wù), 那么剛放進(jìn)行來的block任務(wù)就要等待前面的任務(wù)block執(zhí)行完, 才能執(zhí)行哦).

    ios中獲取主隊(duì)列: dispatch_get_main_queue()
    
    主隊(duì)列中執(zhí)行同步任務(wù)會(huì)造成死鎖哦:
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"會(huì)造成死鎖");
    });
    
  2. 全局并發(fā)隊(duì)列(global queue)--> 并發(fā):

    全局并發(fā)隊(duì)列由所有進(jìn)程共享, 分為(高, 中(默認(rèn)為中), 低, 后臺(tái))四個(gè)優(yōu)先級(jí).

    //程序默認(rèn)的隊(duì)列級(jí)別橘洞,一般不要修改:
    DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
    //HIGH
    DISPATCH_QUEUE_PRIORITY_HIGH
    //LOW
    DISPATCH_QUEUE_PRIORITY_LOW
    //BACKGROUND
    DISPATCH_QUEUE_PRIORITY_BACKGROUND
    
    ios中獲取全局并發(fā)隊(duì)列:dispatch_get_global_queue(0, 0)
    
    全局并發(fā)隊(duì)列執(zhí)行同步任務(wù)會(huì)導(dǎo)致頁面卡頓:
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"會(huì)造成頁面卡頓");
    });
    
    全局并發(fā)隊(duì)列執(zhí)行多個(gè)任務(wù)時(shí), 執(zhí)行的順序是不確定的.
    因?yàn)槿植l(fā)隊(duì)列是由系統(tǒng)默認(rèn)生成的, 故也無法來控制執(zhí)行的繼續(xù)和中斷.
        
    
  3. 自定義隊(duì)列:--> 串行或者并發(fā):

    ios中創(chuàng)建隊(duì)列: dispatch_queue_create()
    //串行隊(duì)列創(chuàng)建
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue_name", DISPATCH_QUEUE_SERIAL);
    
    //自定義串行隊(duì)列 同步執(zhí)行多個(gè)任務(wù)
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    

NSLog(@"current task");
dispatch_sync(serialQueue, ^{
NSLog(@"最先加入自定義串行隊(duì)列");
sleep(2);
});
dispatch_sync(serialQueue, ^{
NSLog(@"次加入自定義串行隊(duì)列");
});
NSLog(@"next task");

//自定義串行隊(duì)列嵌套在執(zhí)行同步任務(wù)會(huì)產(chǎn)生死鎖:
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);

dispatch_sync(serialQueue, ^{
//該代碼段后面的代碼都不會(huì)執(zhí)行,程序被鎖定在這里
NSLog(@"會(huì)執(zhí)行的代碼");
dispatch_sync(serialQueue, ^{
NSLog(@"代碼不執(zhí)行");
});
});

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

//自定義創(chuàng)建并發(fā)隊(duì)列:
dispatch_queue_t conCurrentQueue = dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);

//并發(fā)隊(duì)列執(zhí)行同步是沒有意義的(等同于異步), 這里就不多介紹了. 一般情況下, 使用系統(tǒng)默認(rèn)的全局并發(fā)隊(duì)列就已經(jīng)足夠了, 
//推薦使用系統(tǒng)默認(rèn)的全局并發(fā)隊(duì)列.
```
  1. 隊(duì)列組(group queue):

    將多個(gè)線程進(jìn)行分組, 最大的好處就是可以獲知所有進(jìn)程的完成情況.
    使用場(chǎng)景: 比如說 同時(shí)下載多張圖片時(shí), 有這么一個(gè)需求:要等所有的圖片下載完畢后, 才能去更新UI(回到主線程或者去執(zhí)行其他操作), 這時(shí)候就要用到隊(duì)列組了.

```
ios獲取隊(duì)列組: dispatch_group_create()
通過dispatch_group_notify 可以對(duì)隊(duì)列組中的所有線程進(jìn)行監(jiān)聽進(jìn)程完成情況.

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");
```

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

  1. 延時(shí)方法:

    dispatch_after(time, queue, ^{});
    
    dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_after(delayTime, mainQueue, ^{
        NSLog(@"3秒后添加到主線程中執(zhí)行...");
    });
    
  2. 多次執(zhí)行某一任務(wù):

    dispatch_apply(count, queue, ^(size_t index));
    
    為了不阻塞主線程, 一般dispatch_apply放在異步并行隊(duì)列中執(zhí)行,
    執(zhí)行完了切到主隊(duì)列中再次執(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));
NSThread.sleep(1);
});
NSLog(@"dispatch_apply 執(zhí)行完成");
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
NSLog(@"回到主線程更新UI");
});
});
NSLog(@"next task");

//注意: 嵌套使用dispatch_apply會(huì)導(dǎo)致死鎖。
```
  1. 只執(zhí)行一次的代碼:

    dispatch_once保證在app運(yùn)行期間, block中的代碼只執(zhí)行一次
    ios最常用就是 單例模式
    
    如Person類單例創(chuàng)建方法:
    
    static Person *person = nil;
    + (instanceType)sharedManager {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            person = [self alloc] init];
        });
        return person;
    }
    
  2. 柵欄函數(shù): 類似隊(duì)列組的作用.

    dispatch_barrier_async(queue, ^{});
    
    用法:  在并行隊(duì)列中, 等待在dispatch_barrier_async之前加入隊(duì)列的任務(wù)(并發(fā))全部執(zhí)行完畢, 
    再去執(zhí)行dispatch_barrier_async之后添加進(jìn)行的任務(wù)(注意是并發(fā)執(zhí)行的任務(wù)哦).
    
    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");
});
//等之前的任務(wù)執(zhí)行完畢才會(huì)執(zhí)行下面的任務(wù)哦.
dispatch_barrier_async(conCurrentQueue, ^{
NSLog(@"dispatch barrier");
});
dispatch_async(conCurrentQueue, ^{
NSLog(@"dispatch 3");
});
dispatch_async(conCurrentQueue, ^{
NSLog(@"dispatch 4");
});
```

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市耕漱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抬伺,老刑警劉巖螟够,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異峡钓,居然都是意外死亡妓笙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門能岩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寞宫,“玉大人,你說我怎么就攤上這事拉鹃”哺常” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵膏燕,是天一觀的道長钥屈。 經(jīng)常有香客問我,道長坝辫,這世上最難降的妖魔是什么篷就? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮近忙,結(jié)果婚禮上竭业,老公的妹妹穿的比我還像新娘智润。我一直安慰自己,他們只是感情好未辆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布做鹰。 她就那樣靜靜地躺著,像睡著了一般鼎姐。 火紅的嫁衣襯著肌膚如雪钾麸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天炕桨,我揣著相機(jī)與錄音饭尝,去河邊找鬼。 笑死献宫,一個(gè)胖子當(dāng)著我的面吹牛钥平,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姊途,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涉瘾,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了捷兰?” 一聲冷哼從身側(cè)響起立叛,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贡茅,沒想到半個(gè)月后秘蛇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顶考,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年赁还,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驹沿。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡艘策,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渊季,到底是詐尸還是另有隱情朋蔫,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布梭域,位于F島的核電站斑举,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏病涨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一璧坟、第九天 我趴在偏房一處隱蔽的房頂上張望既穆。 院中可真熱鬧赎懦,春花似錦、人聲如沸幻工。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽囊颅。三九已至当悔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間踢代,已是汗流浹背盲憎。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胳挎,地道東北人饼疙。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像慕爬,于是被迫代替她去往敵國和親窑眯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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