什么是進(jìn)程
進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序
每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)
比如同時(shí)打開(kāi)迅雷、Xcode,系統(tǒng)就會(huì)分別啟動(dòng)2個(gè)進(jìn)程
什么是線程
1個(gè)進(jìn)程要想執(zhí)行任務(wù),必須得有線程(每1個(gè)進(jìn)程至少要有1條線程)
一個(gè)進(jìn)程(程序)的所有任務(wù)都在線程中執(zhí)行
比如使用酷狗播放音樂(lè)、使用迅雷下載電影,都需要在線程中執(zhí)行
1個(gè)線程中任務(wù)的執(zhí)行是串行的
如果要在1個(gè)線程中執(zhí)行多個(gè)任務(wù),那么只能一個(gè)一個(gè)地按順序執(zhí)行這些任務(wù)
也就是說(shuō),在同一時(shí)間內(nèi),1個(gè)線程只能執(zhí)行1個(gè)任務(wù)

什么是多線程
- 1個(gè)進(jìn)程中可以開(kāi)啟多條線程,每條線程可以并行(同時(shí))執(zhí)行不同的任務(wù)
- 多線程技術(shù)可以提高程序的執(zhí)行效率
- 比如同時(shí)開(kāi)啟3條線程分別下載3個(gè)文件(分別是文件A、文件B、文件C

多線程的原理
- 同一時(shí)間,CPU只能處理1條線程,只有1條線程在工作(執(zhí)行)
- 多線程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多條線程之間調(diào)度(切換)
- 如果CPU調(diào)度線程的時(shí)間足夠快,就造成了多線程并發(fā)執(zhí)行的假象
如果線程非常非常多,會(huì)發(fā)生什么情況?
CPU會(huì)在N多線程之間調(diào)度,CPU會(huì)累死,消耗大量的CPU資源
每條線程被調(diào)度執(zhí)行的頻次會(huì)降低(線程的執(zhí)行效率降低)
多線程的優(yōu)點(diǎn)
- 能適當(dāng)提高程序的執(zhí)行效率
- 能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)
多線程的缺點(diǎn)
- 創(chuàng)建線程是有開(kāi)銷(xiāo)的,iOS下主要成本包括:內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)、??臻g(子線程512KB、主線程1MB,也可以使用-setStackSize:設(shè)置,但必須是4K的倍數(shù),而且最小是16K),創(chuàng)建線程大約需要90毫秒的創(chuàng)建時(shí)間
- 如果開(kāi)啟大量的線程,會(huì)降低程序的性能
- 線程越多,CPU在調(diào)度線程上的開(kāi)銷(xiāo)就越大
- 程序設(shè)計(jì)更加復(fù)雜:比如線程之間的通信、多線程的數(shù)據(jù)共享
什么是主線程
一個(gè)iOS程序運(yùn)行后,默認(rèn)會(huì)開(kāi)啟1條線程,稱為“主線程”或“UI線程”
-
主線程的主要作用
- 顯示\刷新UI界面
- 處理UI事件(比如點(diǎn)擊事件、滾動(dòng)事件、拖拽事件等)
-
主線程的使用注意
- 別將比較耗時(shí)的操作放到主線程中
- 耗時(shí)操作會(huì)卡住主線程,嚴(yán)重影響UI的流暢度,給用戶一種“卡”的壞體驗(yàn)
在主線程放一個(gè)需要耗時(shí)10秒的操作;用戶在第五秒的時(shí)候點(diǎn)擊屏幕按鈕,在第十秒的時(shí)候才會(huì)做出響應(yīng);造成卡頓現(xiàn)象;

- 在ios領(lǐng)域里面真正的多線程技術(shù)只有這兩個(gè)pthread和NSthread;
GCD不能直接操作多線程;屬于并發(fā)技術(shù);NSOperation也是一樣,是操作隊(duì)列的,與線程無(wú)關(guān),線程部分GCD已經(jīng)幫你封裝好了!
- GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷(xiāo)毀線程)
- 我們只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫(xiě)任何線程管理代碼
- 蘋(píng)果不建議開(kāi)發(fā)者使用多線程技術(shù);
鼓勵(lì)使用并發(fā)技術(shù);
NSThread
- 創(chuàng)建線程的幾種方法
// 創(chuàng)建一個(gè)NSThread
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo:) object:@"Thread"];
//線程就緒-之后等著cpu調(diào)度;
[thread start];
//demo函數(shù)在子線程執(zhí)行
-(void)demo:(id)obj{
for (int i = 0; i < 2; i++) {
//number !=1
NSLog(@"%@",[NSThread currentThread]);
//[NSThread currentThread] 打印當(dāng)前線程
//返回一個(gè)Thread對(duì)象,里面有number和name屬性
//number == 1 說(shuō)明是主線程 number != 1 就是其他線程
}
}
//detach ==> 分離
//分離出一條子線程
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"Detach"];
//InBackground 就是在后臺(tái)(子線程)運(yùn)行!!
//是NSObject的分類(lèi) 意味著所有的繼承NSObject的都可以使用這個(gè)方法
//非常方便.不用NSThread對(duì)象
[self performSelectorInBackground:@selector(demo:) withObject:@"background"];
線程的狀態(tài)

- 新建一條線程,線程在可調(diào)度線程池里 (由cpu進(jìn)行調(diào)度的)
- 當(dāng) [thread start] 的時(shí)候,在可調(diào)度線程池里的線程都處于就緒狀態(tài)
- cpu調(diào)度處于就緒狀態(tài)的線程
- 運(yùn)行狀態(tài)
- 線程執(zhí)行完畢之后線程在可調(diào)度線程池取出,干掉;
線程阻塞
- 當(dāng)運(yùn)行滿足某個(gè)條件,會(huì)讓線程"睡一會(huì)
提示:sleep 方法是類(lèi)方法,會(huì)直接休眠當(dāng)前線程!!
//當(dāng)前線程睡兩秒
[NSThread sleepForTimeInterval:2.0];
//一旦強(qiáng)行終止線程,后續(xù)的所有代碼都不會(huì)被執(zhí)行
//注意:在終止線程之前,應(yīng)該要釋放之前分配的對(duì)象!!
[NSThread exit];
//創(chuàng)建線程
NSThread * t = [[NSThread alloc]initWithTarget:self selector:@selector(theadStatus) object:nil];
//線程就緒(CPU翻牌)
[t start];
-(void)theadStatus{
for (int i = 0; i < 20;i++) {
//阻塞,當(dāng)運(yùn)行滿足某個(gè)條件,會(huì)讓線程"睡一會(huì)"
//提示:sleep 方法是類(lèi)方法,會(huì)直接休眠當(dāng)前線程!!
if (i == 8) {
NSLog(@"睡一會(huì)");
//睡的是子線程
//注意!!! exit會(huì)殺掉主線程!但是APP不會(huì)掛掉!!
[NSThread sleepForTimeInterval:2.0];
}
NSLog(@"%@ %d",[NSThread currentThread],i);
//當(dāng)線程滿足某一個(gè)條件時(shí),可以強(qiáng)行終止的
//exit 類(lèi)方法,哥么終止當(dāng)前線程!!!!
if (i == 15) {
[NSThread exit]; //線程就就處于死亡狀態(tài)了
}
}
NSLog(@"能來(lái)嗎???"); //來(lái)不了了
}
threadPriority //線程優(yōu)先級(jí)
- 優(yōu)先級(jí)只是保證cpu調(diào)度的可能性會(huì)高!
用優(yōu)先級(jí)來(lái)控制線程得執(zhí)行順序是不理性的!
- 建議:再開(kāi)發(fā)的時(shí)候不要修改優(yōu)先級(jí);
在多線程的開(kāi)發(fā)中,不要相信一次的運(yùn)行結(jié)果!
多線程的目的
- 將耗時(shí)操作放在后臺(tái),不阻塞UI線程
多線程的安全隱患
-
1塊資源可能會(huì)被多個(gè)線程共享,也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源
- 比如多個(gè)線程訪問(wèn)同一個(gè)對(duì)象、同一個(gè)變量、同一個(gè)文件
當(dāng)多個(gè)線程訪問(wèn)同一塊資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題
安全隱患的解決 - 互斥鎖
- 一個(gè)線程訪問(wèn)一個(gè)資源時(shí),先給他上鎖,訪問(wèn)完成之后再解鎖讓其他線程訪問(wèn);
- 互斥鎖 -- 保證鎖內(nèi)的代碼,同一時(shí)間,只有一條線程執(zhí)行!
- 互斥鎖 的范圍 應(yīng)該盡量小,范圍大了 效率就差!!
//互斥鎖
@synchronized (self) {
//括號(hào)里是 同一時(shí)間只能有一條線程執(zhí)行的代碼
};
好比你去上廁所,你把門(mén)鎖上了,別人只能在外面等著,等你操作完廁所,別人才能進(jìn)來(lái);如果你不鎖門(mén),兩個(gè)人在廁所后果不堪設(shè)想!??
原子屬性
- atomic-用來(lái)保護(hù)線程安全(多線程執(zhí)行寫(xiě)入操作的時(shí)候,保證同一時(shí)間只有一個(gè)線程執(zhí)行寫(xiě)入 只對(duì)寫(xiě)入操作上鎖,讀不上鎖)
- 單寫(xiě)多讀的原子屬性(只能單個(gè)線程寫(xiě),但可以多個(gè)線程讀)
- 實(shí)際上原子屬性內(nèi)部有一把鎖叫 - 自旋鎖
- 自旋鎖&互斥鎖異同
-
共同點(diǎn)
- 都能保證線程安全
-
不同點(diǎn)
- 互斥鎖:被鎖在外面的線程,處于休眠狀態(tài);等待鎖打開(kāi),然后被喚醒;
- 自旋鎖:被鎖在外面的線程,用死循環(huán)的方式,等待鎖打開(kāi);
-
- 自旋鎖&互斥鎖異同
- 不管什么鎖,都很消耗性能,效率不高;
想要模擬原子屬性的時(shí)候就在set方法里加了一把鎖;
@synchronized
如果不寫(xiě)nonatomic | atomic默認(rèn)是atomic
線程間的通信方法
- 就這五個(gè)方法
- 把SEL丟到某一個(gè)線程去執(zhí)行
/**
waitUntilDone : 當(dāng)前線程是否等待SEL執(zhí)行完再繼續(xù)
*/
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
GCD(重點(diǎn))
GCD并不是多線程技術(shù);屬于并發(fā)解決技術(shù);
- GCD是蘋(píng)果為了適配多核的并行運(yùn)算提出的解決方案
- GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)
- GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷(xiāo)毀線程)
- 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫(xiě)任何線程管理代碼
GCD的使用就2個(gè)步驟
定制任務(wù)
將任務(wù)添加到隊(duì)列中
GCD中有2個(gè)核心概念
- 任務(wù):執(zhí)行什么操作
- 執(zhí)行任務(wù)方式有兩種; 同步/異步
- 同步:不會(huì)到線程池里面去獲取子線程!
- 異步:只要有任務(wù),就會(huì)到線程池取子線程!(主隊(duì)列除外!)
- 執(zhí)行任務(wù)方式有兩種; 同步/異步
- 隊(duì)列:用來(lái)存放任務(wù)
- 串行:一個(gè)接一個(gè)的調(diào)度任務(wù)
- 并行:可以同時(shí)調(diào)度多個(gè)任務(wù)
GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出,放到對(duì)應(yīng)的線程中執(zhí)行
任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出,后進(jìn)后出
/**
同步執(zhí)行方法,這句話不執(zhí)行完,就不會(huì)執(zhí)行下一個(gè)任務(wù),同步執(zhí)行不會(huì)開(kāi)啟線程
*/
//1.創(chuàng)建隊(duì)列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//2.任務(wù)添加到隊(duì)列中
//2.1 定義任務(wù) -- block
void(^task)() = ^{
NSLog(@"%@",[NSThread currentThread]);
};
//2.2 添加任務(wù)到隊(duì)列,并且會(huì)執(zhí)行
dispatch_sync(q, task);
/**
異步執(zhí)行任務(wù) 哥么如果任務(wù)沒(méi)有執(zhí)行完畢,可以不用等待,異步執(zhí)行下一個(gè)任務(wù)
具備開(kāi)啟線程的能力! 異步通常又是多線程的代名詞!!
*/
-(void)gcdDemo2{
//1.創(chuàng)建隊(duì)列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//2 定義任務(wù) -- block
void(^task)() = ^{
NSLog(@"%@",[NSThread currentThread]);
};
//3. 添加任務(wù)到隊(duì)列
dispatch_async(q, task);
}
//線程間通信-切換子線程到主線程更新UI
//指定任務(wù)執(zhí)行方法 -- 異步
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//耗時(shí)操作
NSLog(@"%@",[NSThread currentThread]);
//更新UI 主隊(duì)列,就是專門(mén)負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊(duì)列!
//dispatch_get_main_queue 主隊(duì)列 住能操作主線程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI%@",[NSThread currentThread]);
});
});
//MARK:串行隊(duì)列,同步任務(wù)
/**
* 不會(huì)開(kāi)啟線程,會(huì)順序執(zhí)行
*/
-(void)gcdDemo1{
//1.隊(duì)列 - 串行
/**
1."ios" 隊(duì)列名稱:
2. NULL 隊(duì)列的屬性: DISPATCH_QUEUE_SERIAL 表示串行!
*/
dispatch_queue_t q = dispatch_queue_create("ios", NULL);
//2.同步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
//MARK: 串行隊(duì)列,異步任務(wù)
-(void)gcdDemo2{
/**
會(huì)開(kāi)幾條線程?會(huì)順序執(zhí)行嗎?
開(kāi)一條線程,順序執(zhí)行
*/
//1.隊(duì)列 - 串行
dispatch_queue_t q = dispatch_queue_create("tanzhouios", NULL);
//2.異步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
NSLog(@"%d------------",i);
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//哥么在主線程!
NSLog(@"come here");
}
//MARK : 并發(fā)隊(duì)列,異步執(zhí)行
-(void)gcdDemo3{
//1.隊(duì)列 - 并發(fā) DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("tanzhouios", DISPATCH_QUEUE_CONCURRENT);
//2.異步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//哥么在主線程!
NSLog(@"come here");
}
//MARK : 并發(fā)隊(duì)列,同步執(zhí)行 和 串行隊(duì)列,同步執(zhí)行 效果一樣!
-(void)gcdDemo4{
// 會(huì)開(kāi)線程嗎? 順序執(zhí)行? come here?
// 不會(huì) 順序 最后
//1.隊(duì)列 - 并發(fā) DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t q = dispatch_queue_create("tanzhouios", DISPATCH_QUEUE_CONCURRENT);
//2.同步執(zhí)行任務(wù)
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
//哥么在主線程!
NSLog(@"come here");
}
小結(jié):
只有異步才會(huì)開(kāi)啟子線程!同步不開(kāi)啟!
開(kāi)幾條線程,取決于隊(duì)列,串行開(kāi)一條,并發(fā)可以開(kāi)多條(異步)
個(gè)人理解鎖這個(gè)問(wèn)題
線程鎖:是多條線程同時(shí)訪問(wèn)一個(gè)資源時(shí)需要一個(gè)互斥鎖;
GDC-死鎖:主隊(duì)列是一個(gè)串行隊(duì)列;在主隊(duì)列里添加一個(gè)同步任務(wù)會(huì)造成死鎖;互相等待的局面;
//主隊(duì)列是專門(mén)負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊(duì)列 --> 不會(huì)開(kāi)線程
//1.隊(duì)列 --> 已啟動(dòng)主線程,就可以獲取主隊(duì)列
dispatch_queue_t q = dispatch_get_main_queue();
//2.同步任務(wù) ==> 死鎖
//走到這里添加一個(gè)同步任務(wù),主隊(duì)列的任務(wù)是不是要等他執(zhí)行完了在執(zhí)行;
dispatch_sync(q, ^{
NSLog(@"能來(lái)嗎? ");
//主隊(duì)列里的任務(wù)沒(méi)執(zhí)行完呢你想執(zhí)行這同步任務(wù),你是不是要等主隊(duì)列執(zhí)行完了才能執(zhí)行;
});
NSLog(@"come here");

注意:
主隊(duì)列不是全局隊(duì)列
全局隊(duì)列:dispatch_get_global_queue
全局隊(duì)列本質(zhì)上是并發(fā)隊(duì)列
主隊(duì)列:dispatch_get_main_queue()
主隊(duì)列是一個(gè)串行隊(duì)列
主隊(duì)列是串行的 主隊(duì)列里的任務(wù)默認(rèn)只有一個(gè)(不手動(dòng)添加的話) 這個(gè)任務(wù)是同步的 主隊(duì)列里直接添加一個(gè)異步任務(wù) 不開(kāi)開(kāi)啟子線程
主隊(duì)列只負(fù)責(zé)主線程
//主隊(duì)列是專門(mén)負(fù)責(zé)在主線程上調(diào)度任務(wù)的隊(duì)列 --> 不會(huì)開(kāi)線程
//1.隊(duì)列 --> 一啟動(dòng)主線程,就可以獲取主隊(duì)列
dispatch_queue_t q = dispatch_get_main_queue();
//2.異步任務(wù)
dispatch_async(q, ^{
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"come here");
//全局隊(duì)列
/* 參數(shù)
1. 涉及到系統(tǒng)適配
iOS 8 服務(wù)質(zhì)量
QOS_CLASS_USER_INTERACTIVE 用戶交互(希望線程快速被執(zhí)行,不要用好使的操作)
QOS_CLASS_USER_INITIATED 用戶需要的(同樣不要使用耗時(shí)操作)
QOS_CLASS_DEFAULT 默認(rèn)的(給系統(tǒng)來(lái)重置隊(duì)列的)
QOS_CLASS_UTILITY 使用工具(用來(lái)做耗時(shí)操作)
QOS_CLASS_BACKGROUND 后臺(tái)
QOS_CLASS_UNSPECIFIED 沒(méi)有指定優(yōu)先級(jí)
iOS 7 調(diào)度的優(yōu)先級(jí)
- DISPATCH_QUEUE_PRIORITY_HIGH 2 高優(yōu)先級(jí)
- DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認(rèn)優(yōu)先級(jí)
- DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優(yōu)先級(jí)
- DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺(tái)優(yōu)先級(jí)
提示:尤其不要選擇BACKGROUND 優(yōu)先級(jí),服務(wù)質(zhì)量,線程執(zhí)行會(huì)慢到令人發(fā)指!!!
2. 為未來(lái)使用的一個(gè)保留,現(xiàn)在始終給0.
老項(xiàng)目中,一般還是沒(méi)有淘汰iOS 7 ,沒(méi)法使用服務(wù)質(zhì)量
*/
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//以第一個(gè)0用來(lái)設(shè)置優(yōu)先級(jí)
//第二個(gè)0還沒(méi)有定義
/*
全局隊(duì)列 & 并發(fā)隊(duì)列 區(qū)別
1> 名稱,并發(fā)隊(duì)列取名字,適合于企業(yè)開(kāi)發(fā)跟蹤錯(cuò)誤
2> release,在MRC 并發(fā)隊(duì)列 需要使用的
dispatch_release(q);//ARC 情況下不需要release !
全局隊(duì)列 & 串行隊(duì)列
全局隊(duì)列: 并發(fā),能夠調(diào)度多個(gè)線程,執(zhí)行效率高
- 費(fèi)電
串行隊(duì)列:一個(gè)一個(gè)執(zhí)行,執(zhí)行效率低
- 省點(diǎn)
判斷依據(jù):用戶上網(wǎng)方式
- WIFI : 可以多開(kāi)線程
- 流量 : 盡量少開(kāi)線程
*/
//1.隊(duì)列
dispatch_queue_t q = dispatch_queue_create("tanzhou", DISPATCH_QUEUE_CONCURRENT);
//2.全局隊(duì)列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
延遲執(zhí)行
/**
從現(xiàn)在開(kāi)始,進(jìn)過(guò)多少納秒之后,讓 queue隊(duì)列,調(diào)度 block 任務(wù),異步執(zhí)行!
參數(shù):
1.when
2.queue
3.block
*/
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.00003 * NSEC_PER_SEC));
dispatch_after(when, dispatch_queue_create("tanzhou", NULL), ^{
NSLog(@"%@",[NSThread currentThread]);
});
GCD:執(zhí)行一次 常用于單利
//蘋(píng)果提供的 一次執(zhí)行機(jī)制,不僅能夠保證一次執(zhí)行!而且是線程安全的!!
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
//蘋(píng)果推薦使用 gcd 一次執(zhí)行,效率高
//不要使用互斥鎖,效率低!
dispatch_once(&onceToken, ^{
//只會(huì)執(zhí)行一次!!
NSLog(@"執(zhí)行了%@",[NSThread currentThread]);
});
GCD調(diào)度組
//1.隊(duì)列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
//2.調(diào)度組
dispatch_group_t g = dispatch_group_create();
//3.添加任務(wù),讓隊(duì)列調(diào)度,任務(wù)執(zhí)行情況,最后通知群組
dispatch_group_async(g, q, ^{
NSLog(@"download A%@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download B%@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"download C%@",[NSThread currentThread]);
});
//4.所有任務(wù)執(zhí)行完畢后,通知
//用一個(gè)調(diào)度組,可以監(jiān)聽(tīng)全局隊(duì)列的任務(wù),主隊(duì)列去執(zhí)行最后的任務(wù)
//dispatch_group_notify 本身也是異步的!
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
//更新UI,通知用戶
NSLog(@"OK %@",[NSThread currentThread]);
});