iOS多線程-NSThread

一、基本概念

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.

線程用于指代一個獨立執行的代碼路徑
進程用于指代一個可執行程序,他可以包含多個線程

Paste_Image.png

3.同步和異步主要影響:能不能開啟新的線程

1 同步:只是在當前線程中執行任務,不具備開啟新線程的能力
2 異步:可以在新的線程中執行任務,具備開啟新線程的能力

4.并發和串行主要影響:任務的執行方式

1 并發:多個任務并發(同時)執行
2 串行:一個任務執行完畢后,再執行下一個任務

注意:
  1. 一個進程可有多個線程。
  2. 一個進程可有多個隊列。
  3. 隊列可分并發隊列和串行隊列。

二.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. 線程操作之------啟動,睡眠,取消,退出

Paste_Image.png
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

![1.jpg](http://upload-images.jianshu.io/upload_images/326255-a503a03b9938f3ad.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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多線程對比

  1. NSThread

每個NSThread對象對應一個線程,真正最原始的線程。

1)優點:NSThread 輕量級最低,相對簡單。

2)缺點:手動管理所有的線程活動,如生命周期、線程同步、睡眠等。

  1. NSOperation

自帶線程管理的抽象類。

1)優點:自帶線程周期管理,操作上可更注重自己邏輯。

2)缺點:面向對象的抽象類,只能實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。

  1. GCD

Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法。

1)優點:最高效,避開并發陷阱。

2)缺點:基于C實現。

  1. 選擇小結

1)簡單而安全的選擇NSOperation實現多線程即可。
2)處理大量并發數據,又追求性能效率的選擇GCD。
3)NSThread本人選擇基本上是在做些小測試上使用,當然也可以基于此造個輪子。

更多精彩內容請關注“IT實戰聯盟”哦~~~


IT實戰聯盟.jpg
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容