一、基本概念
1.計算機操作系統都有的基本概念,以下概念簡單方式來描述。
1 進程: 一個具有一定獨立功能的程序關于某個數據集合的一次運行活動。可以理解成一個運行中的應用程序。
2 線程: 程序執行流的最小單元,線程是進程中的一個實體。
3 隊列: 裝載線程任務的隊形結構。
2.蘋果官方定義
The term thread is used to refer to a separate path of execution for code.
The term process is used to refer to a running executable, which can encompass multiple threads.
線程用于指代一個獨立執行的代碼路徑
進程用于指代一個可執行程序,他可以包含多個線程
3.同步和異步主要影響:能不能開啟新的線程
1 同步:只是在當前線程中執行任務,不具備開啟新線程的能力
2 異步:可以在新的線程中執行任務,具備開啟新線程的能力
4.并發和串行主要影響:任務的執行方式
1 并發:多個任務并發(同時)執行
2 串行:一個任務執行完畢后,再執行下一個任務
注意:
- 一個進程可有多個線程。
- 一個進程可有多個隊列。
- 隊列可分并發隊列和串行隊列。
二.NSThread API
1. 線程創建的兩種方式
//直接創建并啟動一個線程去Selector
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//線程創建出來之后需要手動調用-start方法啟動
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
2. 線程操作之------啟動,睡眠,取消,退出
1. 啟動
使用init方式創建需要手動- (void)start;
例子-
//init初始化
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(thread:) object:@"thread"];
// 開啟線程
[thread start];
例子二
//創建并自動開啟方法
[NSThread detachNewThreadSelector:@selector(thread1:) toTarget:self withObject:@"thread1"];
2. 線程睡眠的兩種方法
//根據NSDate傳入睡眠時間
+ (void)sleepUntilDate:(NSDate *)date;
//直接傳入NSTimeInterval
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
NSThread的sleepUntilDate與runloop的runUntilDate:
深入理解RunLoop

sleepUntilDate:相當于執行一個sleep的任務。在執行過程中,即使有其他任務傳入runloop,runloop也不會立即響應,必須sleep任務完成之后,才會響應其他任務
runUntilDate:雖然會阻塞線程,阻塞過程中并不妨礙新任務的執行。當有新任務的時候,會先執行接收到的新任務,新任務執行完之后,如果時間到了,再繼續執行runUntilDate:之后的代碼
例子:
//讓線程睡眠2秒(阻塞2秒)
[NSThread sleepForTimeInterval:2];
//直接傳入NSTimeInterval
[NSThread sleepUntilDate:[NSDate datewithTimeIntervalSinceNow:2]];
3.取消
對于線程的取消,NSThread提供了一個取消的方法和一個屬性,調用-cancel方法并不會立刻取消線程,它僅僅是將cancelled屬性設置為YES。cancelled也僅僅是一個用于記錄狀態的屬性。線程取消的功能需要我們在main函數中自己實現
- (void)cancel NS_AVAILABLE(10_5, 2_0);
要實現取消的功能,我們需要自己在線程的main函數中定期檢查isCancelled狀態來判斷線程是否需要退出
,當isCancelled為YES的時候,我們手動退出。如果我們沒有在main函數中檢查isCancelled狀態,那么調用-cancel將沒有任何意義
5.退出
與充滿不確定性的-cancel相比,-exit函數可以讓線程立即退出。
+ (void)exit;
-exit屬于核彈級別終極API,調用之后會立即終止線程,即使任務還沒有執行完成也會中斷。這就非常有可能導致內存泄露等嚴重問題,所以一般不推薦使用。
三. 線程之間的通訊
1. 從主線程把耗時的任務丟給輔助線程,當任務完成之后輔助線程再把結果傳回主線程傳,這些線程通訊一般用的都是perform方法
//將selector丟給主線程執行,可以指定runloop mode
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
//將selector丟給主線程執行,runloop mode默認為common mode
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//將selector丟個指定線程執行,可以指定runloop mode
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array NS_AVAILABLE(10_5, 2_0);
//將selector丟個指定線程執行,runloop mode默認為default mode
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
2. 主線程相關
// 獲得主線程
+ (NSThread *)mainThread;
// 是否為主線程
- (BOOL)isMainThread;
// 是否為主線程
+ (BOOL)isMainThread;
// 獲得當前線程
NSThread *current = [NSThread currentThread];
3. 線程優先級
3.1 NSQualityOfService主要有5個枚舉值,優先級別從高到低排布:
1. NSQualityOfServiceUserInteractive:最高優先級,主要用于提供交互UI的操作,比如處理點擊事件,繪制圖像到屏幕上
2. NSQualityOfServiceUserInitiated:次高優先級,主要用于執行需要立即返回的任務
3. NSQualityOfServiceDefault:默認優先級,當沒有設置優先級的時候,線程默認優先級
4. NSQualityOfServiceUtility:普通優先級,主要用于不需要立即返回的任務
5. NSQualityOfServiceBackground:后臺優先級,用于完全不緊急的任務
一般主線程和沒有設置優先級的線程都是默認優先級。
3.2 線程的調度優先級 priority(優先級)
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
- (double)threadPriority;
- (BOOL)setThreadPriority:(double)p;
調度優先級的取值范圍是0.0 - 1.0,默認0.5,值越大,優先級越高
4. 線程通知
通知相關的三種形式
//由當前線程派生出第一個其他線程時發送,一般一個線程只發送一次
NSString * const NSWillBecomeMultiThreadedNotification;
//這個通知目前沒有實際意義,可以忽略
NSString * const NSDidBecomeSingleThreadedNotification;
//線程退出之前發送這個通知
NSString * const NSThreadWillExitNotification;
例子
- (void)viewdidload{
// 創建線程
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil];
//設置線程優先級
self.thread.qualityOfService = NSQualityOfServiceDefault;
//啟動線程
[self.thread start];
}
- (void)threadMain {
//給線程設置名字. 可以不設置
[[NSThread currentThread] setName:@"myThread"];
// 給線程添加runloop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
//給runloop添加數據源
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
//④:檢查isCancelled
while (![[NSThread currentThread] isCancelled]) {
//⑤啟動runloop
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
}
參考: iOS多線程篇
二、iOS多線程對比
- NSThread
每個NSThread對象對應一個線程,真正最原始的線程。
1)優點:NSThread 輕量級最低,相對簡單。
2)缺點:手動管理所有的線程活動,如生命周期、線程同步、睡眠等。
- NSOperation
自帶線程管理的抽象類。
1)優點:自帶線程周期管理,操作上可更注重自己邏輯。
2)缺點:面向對象的抽象類,只能實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。
- GCD
Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法。
1)優點:最高效,避開并發陷阱。
2)缺點:基于C實現。
- 選擇小結
1)簡單而安全的選擇NSOperation實現多線程即可。
2)處理大量并發數據,又追求性能效率的選擇GCD。
3)NSThread本人選擇基本上是在做些小測試上使用,當然也可以基于此造個輪子。
更多精彩內容請關注“IT實戰聯盟”哦~~~