1.術(shù)語(yǔ)
線程:用于指代獨(dú)立執(zhí)行的代碼段
進(jìn)程:用于指代一個(gè)正在運(yùn)行的可執(zhí)行程序,它可以包含多個(gè)線程。
任務(wù):用于指代抽象的概念,表示需要執(zhí)行工作。
兩個(gè)通用隊(duì)列:
串行隊(duì)列:所有任務(wù)會(huì)在一條線程中執(zhí)行(有可能是當(dāng)前線程也有可能是新開(kāi)辟的線程),并且一個(gè)任務(wù)執(zhí)行完畢后,才開(kāi)始執(zhí)行下一個(gè)任務(wù)。(等待完成)
并行隊(duì)列:可以開(kāi)啟多條線程并行執(zhí)行任務(wù)(但不一定會(huì)開(kāi)啟新的線程),并且當(dāng)一個(gè)任務(wù)放到指定線程開(kāi)始執(zhí)行時(shí),下一個(gè)任務(wù)就可以開(kāi)始執(zhí)行了。(等待發(fā)生)
兩個(gè)特殊隊(duì)列:
主隊(duì)列:系統(tǒng)為我們創(chuàng)建好的一個(gè)串行隊(duì)列,牛逼之處在于它管理必須在主線程中執(zhí)行的任務(wù),屬于有勞保的。
全局隊(duì)列:系統(tǒng)為我們創(chuàng)建好的一個(gè)并行隊(duì)列,使用起來(lái)與我們自己創(chuàng)建的并行隊(duì)列無(wú)本質(zhì)差別。
2NSThread
@interface NSThread : NSObject
//當(dāng)前線程
@property (class, readonly, strong) NSThread *currentThread;
//使用類方法創(chuàng)建線程執(zhí)行任務(wù)
- (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
- (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//判斷當(dāng)前是否為多線程 - (BOOL)isMultiThreaded;
//指定線程的線程參數(shù),例如設(shè)置當(dāng)前線程的斷言處理器。
@property (readonly, retain) NSMutableDictionary *threadDictionary;
//當(dāng)前線程暫停到某個(gè)時(shí)間 - (void)sleepUntilDate:(NSDate *)date;
//當(dāng)前線程暫停一段時(shí)間 - (void)sleepForTimeInterval:(NSTimeInterval)ti;
//退出當(dāng)前線程 - (void)exit;
//當(dāng)前線程優(yōu)先級(jí) - (double)threadPriority;
//設(shè)置當(dāng)前線程優(yōu)先級(jí) - (BOOL)setThreadPriority:(double)p;
//指定線程對(duì)象優(yōu)先級(jí) 0.0~1.0,默認(rèn)值為0.5
@property double threadPriority NS_AVAILABLE(10_6, 4_0);
//服務(wù)質(zhì)量
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
//線程名稱
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
//棧區(qū)大小
@property NSUInteger stackSize NS_AVAILABLE(10_5, 2_0);
//是否為主線程
@property (class, readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);
//獲取主線程
@property (class, readonly, strong) NSThread *mainThread NS_AVAILABLE(10_5, 2_0);
//初始化
- (instancetype)init NS_AVAILABLE(10_5, 2_0) NS_DESIGNATED_INITIALIZER;
//實(shí)例方法初始化,需要再調(diào)用start方法 - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//線程狀態(tài),正在執(zhí)行
@property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);
//線程狀態(tài),正在完成
@property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);
//線程狀態(tài),已經(jīng)取消
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);
//取消,僅僅改變線程狀態(tài),并不能像exist一樣真正的終止線程 - (void)cancel NS_AVAILABLE(10_5, 2_0);
//開(kāi)始 - (void)start NS_AVAILABLE(10_5, 2_0);
//線程需要執(zhí)行的代碼,一般寫子類的時(shí)候會(huì)用到 - (void)main NS_AVAILABLE(10_5, 2_0);
@end
另外,還有一個(gè)NSObject的分類,瞅一眼:
@interface NSObject (NSThreadPerformAdditions)
//隱式的創(chuàng)建并啟動(dòng)線程,并在指定的線程(主線程或子線程)上執(zhí)行方法。 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)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);
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
@end
對(duì)于單核CPU來(lái)說(shuō),不存在真正意義上的并行,所以,多線程執(zhí)行任務(wù),其實(shí)也只是一個(gè)人在干活,CPU的調(diào)度決定了非等待任務(wù)的執(zhí)行速率,同時(shí)對(duì)于非等待任務(wù),多線程并沒(méi)有真正意義提高效率。
線程可以簡(jiǎn)單的認(rèn)為就是一段代碼+運(yùn)行時(shí)數(shù)據(jù)。
同步執(zhí)行會(huì)在當(dāng)前線程執(zhí)行任務(wù),不具備開(kāi)辟線程的能力或者說(shuō)沒(méi)有必要開(kāi)辟新的線程。并且,同步執(zhí)行必須等到Block函數(shù)執(zhí)行完畢,dispatch函數(shù)才會(huì)返回,從而阻塞同一串行隊(duì)列中外部方法的執(zhí)行。
異步執(zhí)行dispatch函數(shù)會(huì)直接返回,Block函數(shù)我們可以認(rèn)為它會(huì)在下一幀加入隊(duì)列,并根據(jù)所在隊(duì)列目前的任務(wù)情況無(wú)限下一幀執(zhí)行,從而不會(huì)阻塞當(dāng)前外部任務(wù)的執(zhí)行。同時(shí),只有異步執(zhí)行才有開(kāi)辟新線程的必要,但是異步執(zhí)行不一定會(huì)開(kāi)辟新線程。
只要是隊(duì)列,肯定是FIFO(先進(jìn)先出),但是誰(shuí)先執(zhí)行完要看第1條。
只要是串行隊(duì)列,肯定要等上一個(gè)任務(wù)執(zhí)行完成,才能開(kāi)始下一個(gè)任務(wù)。但是并行隊(duì)列當(dāng)上一個(gè)任務(wù)開(kāi)始執(zhí)行后,下一個(gè)任務(wù)就可以開(kāi)始執(zhí)行。
想要開(kāi)辟新線程必須讓任務(wù)在異步執(zhí)行,想要開(kāi)辟多個(gè)線程,只有讓任務(wù)在并行隊(duì)列中異步執(zhí)行才可以。執(zhí)行方式和隊(duì)列類型多層組合在一定程度上能夠?qū)崿F(xiàn)對(duì)于代碼執(zhí)行順序的調(diào)度。
同步+串行:未開(kāi)辟新線程,串行執(zhí)行任務(wù);同步+并行:未開(kāi)辟新線程,串行執(zhí)行任務(wù);異步+串行:新開(kāi)辟一條線程,串行執(zhí)行任務(wù);異步+并行:開(kāi)辟多條新線程,并行執(zhí)行任務(wù);在主線程中同步使用主隊(duì)列執(zhí)行任務(wù),會(huì)造成死鎖。
對(duì)于多核CPU來(lái)說(shuō),線程數(shù)量也不能無(wú)限開(kāi)辟,線程的開(kāi)辟同樣會(huì)消耗資源,過(guò)多線程同時(shí)處理任務(wù)并不是你想像中的人多力量大。
10.并行隊(duì)列+同步 不生成線程 并行隊(duì)列+異步 生成新的線程
GCD其他函數(shù)用法
- dispatch_after
該函數(shù)用于任務(wù)延時(shí)執(zhí)行,其中參數(shù)dispatch_time_t代表延時(shí)時(shí)長(zhǎng),dispatch_queue_t代表使用哪個(gè)隊(duì)列。如果隊(duì)列未主隊(duì)列,那么任務(wù)在主線程執(zhí)行,如果隊(duì)列為全局隊(duì)列或者自己創(chuàng)建的隊(duì)列,那么任務(wù)在子線程執(zhí)行,代碼如下:
-(void)GCDDelay{
//主隊(duì)列延時(shí)
dispatch_time_t when_main = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
dispatch_after(when_main, dispatch_get_main_queue(), ^{
NSLog(@"main_%@",[NSThread currentThread]);
});
//全局隊(duì)列延時(shí)
dispatch_time_t when_global = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC));
dispatch_after(when_global, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"global_%@",[NSThread currentThread]);
});
//自定義隊(duì)列延時(shí)
dispatch_time_t when_custom = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(when_custom, self.serialQueue, ^{
NSLog(@"custom_%@",[NSThread currentThread]);
});
}