ios中多線程GCD(Grand Center Dispatch)
- 特別注意ios中主線程又稱作為 UI線程, 主要任務(wù)就是處理UI事件, 即顯示和刷新UI,
- 只有主線程具有直接修改UI的能力, 那些耗時(shí)的(從網(wǎng)絡(luò)獲取數(shù)據(jù) | 加載圖片 | 數(shù)據(jù)庫讀取 | IO等)操作要放在子線程(又稱為后臺(tái)線程或者異步線程)中處理, 這樣可以提高程序執(zhí)行效率和資源利用率, 最重要的用戶的 UI 的體驗(yàn)也會(huì)很好.
- 死鎖: 兩個(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í)行.
-
主線程隊(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ì)造成死鎖"); });
-
全局并發(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ù)和中斷.
-
自定義隊(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ì)列.
```
-
隊(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方法
-
延時(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í)行..."); });
-
多次執(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)致死鎖。
```
-
只執(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; }
-
柵欄函數(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");
});
```