1. 進(jìn)程與線程
- 進(jìn)程概念: 進(jìn)程是程序在計(jì)算機(jī)的一次執(zhí)行活動(dòng),打開一個(gè)app,就開啟了一個(gè)進(jìn)程,可包含多個(gè)線程
- 線程概念: 獨(dú)立執(zhí)行的代碼段,一個(gè)線程同時(shí)間只能執(zhí)行一次,反之多線程并發(fā)可以同一時(shí)間執(zhí)行多個(gè)任務(wù)
- iOS程序中,主線程(又叫做UI線程) 主要任務(wù)是處理UI事件,顯示和刷新UI,(只有主線程有直接修改UI的能力) 耗時(shí)的操作放在子線程 (又叫做后臺(tái)線程,異步線程). 在iOS中開子線程去處理耗時(shí)的操作,可以有效的提升程序的執(zhí)行效率,提高資源利用率,但是開啟線程會(huì)占用一定的內(nèi)存, (主線程的堆棧大小是1M,第二個(gè)線程開始都是512KB,并且該值不能通過編譯器開關(guān)或線程API 函數(shù)來更改),降低程序的性能. 所以一般不要同時(shí)開很多線程
2. 線程相關(guān)
- 同步線程: 同步線程會(huì)阻塞當(dāng)前線程去執(zhí)行線程內(nèi)的任務(wù),執(zhí)行完之后才會(huì)返回當(dāng)前線程
- 串行隊(duì)列: 線程任務(wù)按先后順序逐個(gè)執(zhí)行 (需要等待前面的任務(wù)執(zhí)行完畢后再執(zhí)行新的任務(wù))
- 并發(fā)隊(duì)列: 多個(gè)任務(wù)按添加順序一起開始執(zhí)行 (不用等待前面的任務(wù)執(zhí)行完后再執(zhí)行新的任務(wù)),但是添加間隔往往忽略不計(jì),所以看起來像是儀器執(zhí)行的
- 并發(fā) &并行: 并行是基于多核設(shè)備的,并行不一定是并發(fā)的,并發(fā)不一定是并行的
- 死鎖: 兩個(gè)或者多個(gè)線程都要等待對(duì)方完成某個(gè)操作才能進(jìn)行下一步,這時(shí)就會(huì)發(fā)生死鎖
*Thread Safe (線程安全) : 一段線程安全的代碼(對(duì)象), 可以同時(shí)被多個(gè)線程或并發(fā)的任務(wù)調(diào)度,不會(huì)產(chǎn)生問題, 非線程安全的只能按次序被訪問 - 所有Mutable對(duì)象都是非線程安全的,所有Immutable對(duì)象都是線程安全的龙考,使用Mutable對(duì)象狡门,一定要用同步鎖來同步訪問(@synchronized)
3. GCD的三種隊(duì)列類型
GCD編程的核心就是dispatch隊(duì)列, dispatch block 的執(zhí)行最終都會(huì)放到某個(gè)隊(duì)列中去進(jìn)行
The main queue (主線程串行隊(duì)列): 與主線程功能相同,提交至Main queue 的任務(wù)會(huì)在主線程中執(zhí)行
Main queue 可以通過dispatch_get_main_queue()來獲取Global queue(全局并發(fā)隊(duì)列): 全局并發(fā)隊(duì)列由整個(gè)進(jìn)程共享,有高 中(默認(rèn)),低 后臺(tái)四個(gè)優(yōu)先級(jí)別
Global queue 可以通過調(diào)用dispatch_get_global_queue 函數(shù)來獲取Custom queue (自定義隊(duì)列): 可以為串行毙死,也可以為并發(fā)想罕。
Custom queue 可以通過dispatch_queue_create()來獲取后德;Group queue (隊(duì)列組):將多線程進(jìn)行分組坷檩,最大的好處是可獲知所有線程的完成情況。
Group queue 可以通過調(diào)用dispatch_group_create()來獲取赡艰,通過dispatch_group_notify,可以直接監(jiān)聽組里所有線程完成情況售淡。
4.The main queue(主線程串行隊(duì)列)
dispatch_sync 同步執(zhí)行任務(wù)函數(shù),不會(huì)開啟新的線程,dispatch_async 異步執(zhí)行任務(wù)函數(shù),會(huì)開啟新的線程
- 獲取主線程串行隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
2.主線程串行隊(duì)列同步執(zhí)行任務(wù),在主線程運(yùn)行時(shí)慷垮,會(huì)產(chǎn)生死鎖
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue,^{
NSLog("MainQueue");
});
程序一直處于等待狀態(tài)揖闸,block中的代碼將執(zhí)行不到
3.主線程串行隊(duì)列異步執(zhí)行任務(wù),在主線程運(yùn)行料身,不會(huì)產(chǎn)生死鎖汤纸。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue,^{
NSLog("MainQueue");
});
程序正常運(yùn)行,block中的代碼正常運(yùn)行
4.從子線程芹血,異步返回主線程更新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)生成的啃擦,所以無法調(diào)用dispatch_resume()和dispatch_suspend()來控制執(zhí)行繼續(xù)或中斷囊蓝。
5. Global queue (全局并發(fā)隊(duì)列)
耗時(shí)的操作,比如讀取網(wǎng)絡(luò)數(shù)據(jù),IO,數(shù)據(jù)庫讀寫等, 我們會(huì)在另一個(gè)線程中處理這些操作, 然后通知主線程更新界面
1.獲取全局并發(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);
2.全局并發(fā)隊(duì)列同步執(zhí)行任務(wù),在主線程會(huì)導(dǎo)致頁面卡頓
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");
依次輸出: "current task" "sleep 2.0s","next task",2s之后才會(huì)執(zhí)行block后面的代碼,會(huì)造成頁面卡頓
- 全局并發(fā)隊(duì)列異步執(zhí)行任務(wù),在主線程運(yùn)行,會(huì)開啟新的子線程取執(zhí)行任務(wù),頁面不會(huì)卡頓
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSLog(@"current task"); dispatch_async(globalQueue, ^{ //異步執(zhí)行,不會(huì)等待2s ,造成卡頓 sleep(2.0); NSLog(@"sleep 2.0s"); }); NSLog(@"next task");
4.多個(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");
異步線程的執(zhí)行順序是不確定的,幾乎同步執(zhí)行,全局并發(fā)隊(duì)列有系統(tǒng)默認(rèn)生成,令蛉,所以無法調(diào)用dispatch_resume()和dispatch_suspend()來控制執(zhí)行繼續(xù)或中斷聚霜。
6.自定義隊(duì)列(custom queue)
1.自定義串行隊(duì)列
獲取自定義串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("MrRightGen.serialQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"%s",dispatch_queue_get_label(conCurrentQueue)) ;
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ì)列類型祷安,是串行的還是并發(fā)的自定義串行隊(duì)列同步執(zhí)行任務(wù)
dispatch_queue_t serialQueue = dispatch_queue_create("MrRightGen.serialQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"current task"); dispatch_sync(serialQueue, ^{ NSLog(@"最先加入自定義串行隊(duì)列"); sleep(2); }); dispatch_sync(serialQueue, ^{ NSLog(@"次加入自定義串行隊(duì)列"); }); NSLog(@"next task");
當(dāng)前線程等待串行隊(duì)列中的子線程執(zhí)行完成之后再執(zhí)行姥芥,串行隊(duì)列中先進(jìn)來的子線程先執(zhí)行任務(wù),執(zhí)行完成后辆憔,再執(zhí)行隊(duì)列中后面的任務(wù)撇眯。自定義串行隊(duì)列嵌套執(zhí)行同步任務(wù),產(chǎn)生死鎖
dispatch_queue_t serialQueue = dispatch_queue_create("MrRightGen.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("MrRightGen.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("MrRightGen.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
*自定義并發(fā)執(zhí)行同步任務(wù)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("MrRightGen.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"current task"); dispatch_sync(conCurrentQueue, ^{ NSLog(@"先加入隊(duì)列"); }); dispatch_sync(conCurrentQueue, ^{ NSLog(@"次加入隊(duì)列"); }); NSLog(@"next task");
//任務(wù)自上而下依次執(zhí)行自定義并發(fā)隊(duì)列嵌套執(zhí)行同步任務(wù)(不會(huì)產(chǎn)生死鎖玄坦,程序正常運(yùn)行)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("MrRightGen.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"current task");
dispatch_sync(conCurrentQueue, ^{
NSLog(@"先加入隊(duì)列");
dispatch_sync(conCurrentQueue, ^{
NSLog(@"次加入隊(duì)列");
});
});
NSLog(@"next task");自定義并發(fā)隊(duì)列執(zhí)行異步任務(wù)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("MrRightGen.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"current task");
dispatch_async(conCurrentQueue, ^{
NSLog(@"先加入隊(duì)列");
});
dispatch_async(conCurrentQueue, ^{
NSLog(@"次加入隊(duì)列");
});
NSLog(@"next task");
異步執(zhí)行任務(wù),開啟新的子線程绘沉,不影響當(dāng)前線程任務(wù)的執(zhí)行煎楣,并發(fā)隊(duì)列中的任務(wù),幾乎是同步執(zhí)行的车伞,輸出順序不確定
6. 隊(duì)列組 (Group queue)
當(dāng)遇到需要多個(gè)線程并發(fā)執(zhí)行,然后等多個(gè)線程都結(jié)束之后,再匯總執(zhí)行結(jié)果時(shí),可以用group queue
1.使用場(chǎng)景: 同時(shí)下載多個(gè)圖片,所有圖片下載完成之后,去更新UI (需要回到主線程) 或者去處理其他任務(wù) (可以是其他線程隊(duì)列)
2.原理: 使用函數(shù) dispatch_group_create 創(chuàng)建 dispatch group , 然后使用函數(shù) dispatch_group_async 來將要執(zhí)行的block 任務(wù)提交到一個(gè) dispatch queue. 同時(shí)將他們添加到一個(gè)組, 等要執(zhí)行的block 任務(wù)全部執(zhí)行完畢之后,使用 dispatch_group_notify 函數(shù)接收完成時(shí)的消息
3.使用示例:
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");
按此順序依次輸出
4.在當(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");
});
參數(shù)注釋:
第一個(gè)參數(shù)一般是DISPATCH_TIME_NOW择懂,表示從現(xiàn)在開始
第二個(gè)參數(shù)是延時(shí)的具體時(shí)間
延時(shí)1秒可以寫成如下幾種:
NSEC_PER_SEC----每秒有多少納秒
dispatch_time(DISPATCH_TIME_NOW, 1NSEC_PER_SEC);
USEC_PER_SEC----每秒有多少毫秒(注意是指在納秒的基礎(chǔ)上)
dispatch_time(DISPATCH_TIME_NOW, 1000USEC_PER_SEC); //SEC---毫秒
NSEC_PER_USEC----每毫秒有多少納秒。
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC*NSEC_PER_USEC);SEC---納秒
7.GCD中一些系統(tǒng)提供的常用dispatch方法
1.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");
dispatch_after 只是延時(shí)提交block,并不是延時(shí)后立即執(zhí)行,并不能做到精準(zhǔn)控制
- dispatch_once 保證在app運(yùn)行期間,block中的代碼只執(zhí)行一次
經(jīng)典使用場(chǎng)景--單例
//GCD實(shí)現(xiàn)單例功能
+ (ShareManager *)shareManager
{
static ShareManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
@end
ShareManager的使用
#######import "ShareManager.h"
在需要使用的函數(shù)中另玖,直接調(diào)用下面的方法
ShareManager *share = [ShareManager shareManager];
NSLog(@"share is %@",share.someProperty);
最近在學(xué)習(xí)多線程與GCD的使用,看了很多資料,感覺本文寫的比較詳細(xì),故引用之.參考自dullgrass-簡(jiǎn)書