1六、iOS多線程篇:NSThread

1、什麼是NSThread

  NSThread是基於線程使用,輕量級的多線程編程方法(相對GCD和NSOperation),一個NSThread對象表明一個線程,須要手動管理線程的生命週期,處理線程同步等問題。css

2、NSThread方法介紹

1)動態建立
NSThread * newThread = [[NSThread alloc]initWithTarget:self selector:@selector(threadRun) object:nil];

  動態方法返回一個新的thread對象,須要調用start方法來啓動線程編程

2)靜態建立
[NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];

  因爲靜態方法沒有返回值,若是須要獲取新建立的thread,須要在selector中調用獲取當前線程的方法多線程

3)線程開啓
[newThread start];
4)線程暫停
 [NSThread sleepForTimeInterval:1.0]; (以暫停一秒爲例)  [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];

  NSThread的暫停會有阻塞當前線程的效果oop

5)線程取消
[newThread cancel];

  取消線程並不會立刻中止並退出線程,僅僅只做(線程是否須要退出)狀態記錄ui

6)線程中止
[NSThread exit];

  中止方法會當即終止除主線程之外全部線程(不管是否在執行任務)並退出,須要在掌控全部線程狀態的狀況下調用此方法,不然可能會致使內存問題。spa

7)獲取當前線程
 [NSThread currentThread];
8)獲取主線程
 [NSThread mainThread];
9)線程優先級設置

  iOS8之前使用線程

 [NSThread setThreadPriority:1.0];

  這個方法的優先級的數值設置讓人困惑,由於你不知道你應該設置多大的值是比較合適的,所以在iOS8以後,threadPriority添加了一句註釋:To be deprecated; use qualityOfService belowcode

  意思就是iOS8之後推薦使用qualityOfService屬性,經過量化的優先級枚舉值來設置
  qualityOfService的枚舉值以下:
    NSQualityOfServiceUserInteractive:最高優先級,用於用戶交互事件
    NSQualityOfServiceUserInitiated:次高優先級,用於用戶須要立刻執行的事件
    NSQualityOfServiceDefault:默認優先級,主線程和沒有設置優先級的線程都默認爲這個優先級
    NSQualityOfServiceUtility:普通優先級,用於普通任務
    NSQualityOfServiceBackground:最低優先級,用於不重要的任務orm

  好比給線程設置次高優先級:server

[newThread setQualityOfService:NSQualityOfServiceUserInitiated];

3、線程間通訊

  經常使用的有三種:

  一、指定當前線程執行操做
[self performSelector:@selector(threadRun)]; [self performSelector:@selector(threadRun) withObject:nil]; [self performSelector:@selector(threadRun) withObject:nil afterDelay:2.0];
  二、(在其餘線程中)指定主線程執行操做
[self performSelectorOnMainThread:@selector(threadRun) withObject:nil waitUntilDone:YES];

  注意:更新UI要在主線程中進行

  三、(在主線程中)指定其餘線程執行操做
[self performSelector:@selector(threadRun) onThread:newThread withObject:nil waitUntilDone:YES]; //這裏指定爲某個線程 [self performSelectorInBackground:@selector(threadRun) withObject:nil];//這裏指定爲後臺線程

4、線程同步

  線程和其餘線程可能會共享一些資源,當多個線程同時讀寫同一份共享資源的時候,可能會引發衝突。線程同步是指是指在必定的時間內只容許某一個線程訪問某個資源

  iOS實現線程加鎖有NSLock和@synchronized兩種方式

5、線程的建立和使用實例:模擬售票

  情景:某演唱會門票發售,在廣州和北京均開設窗口進行銷售,如下是代碼實現

先監聽線程退出的通知,以便知道線程何時退出
     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExitNotice) name:NSThreadWillExitNotification object:nil];
設置演唱會的門票數量
     _ticketCount = 50;
新建兩個子線程(表明兩個窗口同時銷售門票)
     NSThread * window1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; window1.name = @"北京售票窗口"; [window1 start]; NSThread * window2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; window2.name = @"廣州售票窗口"; [window2 start];
線程啓動後,執行saleTicket,執行完畢後就會退出,爲了模擬持續售票的過程,咱們須要給它加一個循環
     - (void)saleTicket { while (1) { //若是還有票,繼續售賣 if (_ticketCount > 0) { _ticketCount --; NSLog(@"%@", [NSString stringWithFormat:@"剩餘票數:%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]); [NSThread sleepForTimeInterval:0.2]; } //若是已賣完,關閉售票窗口 else { break; } } }
執行結果:
    2016-04-06 19:25:36.637 MutiThread[4705:1371666] 剩餘票數:9 窗口:廣州售票窗口 2016-04-06 19:25:36.637 MutiThread[4705:1371665] 剩餘票數:8 窗口:北京售票窗口 2016-04-06 19:25:36.839 MutiThread[4705:1371666] 剩餘票數:7 窗口:廣州售票窗口 2016-04-06 19:25:36.839 MutiThread[4705:1371665] 剩餘票數:7 窗口:北京售票窗口 2016-04-06 19:25:37.045 MutiThread[4705:1371666] 剩餘票數:5 窗口:廣州售票窗口 2016-04-06 19:25:37.045 MutiThread[4705:1371665] 剩餘票數:6 窗口:北京售票窗口 2016-04-06 19:25:37.250 MutiThread[4705:1371665] 剩餘票數:4 窗口:北京售票窗口 2016-04-06 19:25:37.250 MutiThread[4705:1371666] 剩餘票數:4 窗口:廣州售票窗口 2016-04-06 19:25:37.456 MutiThread[4705:1371666] 剩餘票數:2 窗口:廣州售票窗口 2016-04-06 19:25:37.456 MutiThread[4705:1371665] 剩餘票數:3 窗口:北京售票窗口 2016-04-06 19:25:37.661 MutiThread[4705:1371665] 剩餘票數:1 窗口:北京售票窗口 2016-04-06 19:25:37.661 MutiThread[4705:1371666] 剩餘票數:1 窗口:廣州售票窗口 2016-04-06 19:25:37.866 MutiThread[4705:1371665] 剩餘票數:0 窗口:北京售票窗口 2016-04-06 19:25:37.867 MutiThread[4705:1371666] <NSThread: 0x7fdc91e289f0>{number = 3, name = 廣州售票窗口} Will Exit 2016-04-06 19:25:38.070 MutiThread[4705:1371665] <NSThread: 0x7fdc91e24d60>{number = 2, name = 北京售票窗口} Will Exit

  能夠看到,票的銷售過程當中出現了剩餘數量錯亂的狀況,這就是前面提到的線程同步問題。

  售票是一個典型的須要線程同步的場景,因爲售票渠道有不少,而票的資源是有限的,當多個渠道在短期內賣出大量的票的時候,若是沒有同步機制來管理票的數量,將會致使票的總數和售出票數對應不上的錯誤。

咱們在售票的過程當中給票加上同步鎖:同一時間內,只有一個線程能對票的數量進行操做,當操做完成以後,其餘線程才能繼續對票的數量進行操做。
     - (void)saleTicket { while (1) { @synchronized(self) { //若是還有票,繼續售賣 if (_ticketCount > 0) { _ticketCount --; NSLog(@"%@", [NSString stringWithFormat:@"剩餘票數:%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]); [NSThread sleepForTimeInterval:0.2]; } //若是已賣完,關閉售票窗口 else { break; } } } }
運行結果:
     2016-04-06 19:31:27.913 MutiThread[4718:1406865] 剩餘票數:11 窗口:北京售票窗口 2016-04-06 19:31:28.115 MutiThread[4718:1406866] 剩餘票數:10 窗口:廣州售票窗口 2016-04-06 19:31:28.317 MutiThread[4718:1406865] 剩餘票數:9 窗口:北京售票窗口 2016-04-06 19:31:28.522 MutiThread[4718:1406866] 剩餘票數:8 窗口:廣州售票窗口 2016-04-06 19:31:28.728 MutiThread[4718:1406865] 剩餘票數:7 窗口:北京售票窗口 2016-04-06 19:31:28.929 MutiThread[4718:1406866] 剩餘票數:6 窗口:廣州售票窗口 2016-04-06 19:31:29.134 MutiThread[4718:1406865] 剩餘票數:5 窗口:北京售票窗口 2016-04-06 19:31:29.339 MutiThread[4718:1406866] 剩餘票數:4 窗口:廣州售票窗口 2016-04-06 19:31:29.545 MutiThread[4718:1406865] 剩餘票數:3 窗口:北京售票窗口 2016-04-06 19:31:29.751 MutiThread[4718:1406866] 剩餘票數:2 窗口:廣州售票窗口 2016-04-06 19:31:29.952 MutiThread[4718:1406865] 剩餘票數:1 窗口:北京售票窗口 2016-04-06 19:31:30.158 MutiThread[4718:1406866] 剩餘票數:0 窗口:廣州售票窗口 2016-04-06 19:31:30.363 MutiThread[4718:1406866] <NSThread: 0x7ff0c1637320>{number = 3, name = 廣州售票窗口} Will Exit 2016-04-06 19:31:30.363 MutiThread[4718:1406865] <NSThread: 0x7ff0c1420cb0>{number = 2, name = 北京售票窗口} Will Exit

  能夠看到,票的數量沒有出現錯亂的狀況。

  線程的持續運行和退出

  咱們注意到,線程啓動後,執行saleTicket完畢後就立刻退出了,怎樣能讓線程一直運行呢(窗口一直開放,能夠隨時指派其賣演唱會的門票的任務),答案就是給線程加上runLoop

```
     先監聽線程退出的通知,以便知道線程何時退出
     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExitNotice) name:NSThreadWillExitNotification object:nil];
 ```
//設置演唱會的門票數量 _ticketCount = 50;
新建兩個子線程(表明兩個窗口同時銷售門票)
     NSThread * window1 = [[NSThread alloc]initWithTarget:self selector:@selector(thread1) object:nil]; [window1 start]; NSThread * window2 = [[NSThread alloc]initWithTarget:self selector:@selector(thread2) object:nil]; [window2 start];
接着咱們給線程建立一個runLoop
     - (void)thread1 { [NSThread currentThread].name = @"北京售票窗口"; NSRunLoop * runLoop1 = [NSRunLoop currentRunLoop]; [runLoop1 runUntilDate:[NSDate date]]; //一直運行 } - (void)thread2 { [NSThread currentThread].name = @"廣州售票窗口"; NSRunLoop * runLoop2 = [NSRunLoop currentRunLoop]; [runLoop2 runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]]; //自定義運行時間 }
而後就能夠指派任務給線程了,這裏咱們讓兩個線程都執行相同的任務(售票)
     [self performSelector:@selector(saleTicket) onThread:window1 withObject:nil waitUntilDone:NO]; [self performSelector:@selector(saleTicket) onThread:window2 withObject:nil waitUntilDone:NO];
運行結果:
     2016-04-06 19:43:22.585 MutiThread[4762:1478200] 剩餘票數:11 窗口:北京售票窗口 2016-04-06 19:43:22.788 MutiThread[4762:1478201] 剩餘票數:10 窗口:廣州售票窗口 2016-04-06 19:43:22.993 MutiThread[4762:1478200] 剩餘票數:9 窗口:北京售票窗口 2016-04-06 19:43:23.198 MutiThread[4762:1478201] 剩餘票數:8 窗口:廣州售票窗口 2016-04-06 19:43:23.404 MutiThread[4762:1478200] 剩餘票數:7 窗口:北京售票窗口 2016-04-06 19:43:23.609 MutiThread[4762:1478201] 剩餘票數:6 窗口:廣州售票窗口 2016-04-06 19:43:23.810 MutiThread[4762:1478200] 剩餘票數:5 窗口:北京售票窗口 2016-04-06 19:43:24.011 MutiThread[4762:1478201] 剩餘票數:4 窗口:廣州售票窗口 2016-04-06 19:43:24.216 MutiThread[4762:1478200] 剩餘票數:3 窗口:北京售票窗口 2016-04-06 19:43:24.422 MutiThread[4762:1478201] 剩餘票數:2 窗口:廣州售票窗口 2016-04-06 19:43:24.628 MutiThread[4762:1478200] 剩餘票數:1 窗口:北京售票窗口 2016-04-06 19:43:24.833 MutiThread[4762:1478201] 剩餘票數:0 窗口:廣州售票窗口 2016-04-06 19:43:25.039 MutiThread[4762:1478201] <NSThread: 0x7fe0d3c24360>{number = 3, name = 廣州售票窗口} Will Exit

  能夠看到,當票賣完後,兩個線程並無退出,仍在繼續運行,當到達指定時間後,線程2退出了,若是須要讓線程1退出,須要咱們手動管理。

  好比咱們讓線程完成任務(售票)後自行退出,能夠這樣操做

- (void)saleTicket { while (1) { @synchronized(self) { //若是還有票,繼續售賣 if (_ticketCount > 0) { _ticketCount --; NSLog(@"%@", [NSString stringWithFormat:@"剩餘票數:%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]); [NSThread sleepForTimeInterval:0.2]; } //若是已賣完,關閉售票窗口 else { if ([NSThread currentThread].isCancelled) { break; }else { NSLog(@"售賣完畢"); //給當前線程標記爲取消狀態 [[NSThread currentThread] cancel]; //中止當前線程的runLoop CFRunLoopStop(CFRunLoopGetCurrent()); } } } } }
運行結果:
     2016-04-06 20:08:38.287 MutiThread[4927:1577193] 剩餘票數:10 窗口:北京售票窗口 2016-04-06 20:08:38.489 MutiThread[4927:1577194] 剩餘票數:9 窗口:廣州售票窗口 2016-04-06 20:08:38.690 MutiThread[4927:1577193] 剩餘票數:8 窗口:北京售票窗口 2016-04-06 20:08:38.892 MutiThread[4927:1577194] 剩餘票數:7 窗口:廣州售票窗口 2016-04-06 20:08:39.094 MutiThread[4927:1577193] 剩餘票數:6 窗口:北京售票窗口 2016-04-06 20:08:39.294 MutiThread[4927:1577194] 剩餘票數:5 窗口:廣州售票窗口 2016-04-06 20:08:39.499 MutiThread[4927:1577193] 剩餘票數:4 窗口:北京售票窗口 2016-04-06 20:08:39.700 MutiThread[4927:1577194] 剩餘票數:3 窗口:廣州售票窗口 2016-04-06 20:08:39.905 MutiThread[4927:1577193] 剩餘票數:2 窗口:北京售票窗口 2016-04-06 20:08:40.106 MutiThread[4927:1577194] 剩餘票數:1 窗口:廣州售票窗口 2016-04-06 20:08:40.312 MutiThread[4927:1577193] 剩餘票數:0 窗口:北京售票窗口 2016-04-06 20:08:40.516 MutiThread[4927:1577194] 售賣完畢 2016-04-06 20:08:40.516 MutiThread[4927:1577193] 售賣完畢 2016-04-06 20:08:40.517 MutiThread[4927:1577193] <NSThread: 0x7fb719d54000>{number = 2, name = 北京售票窗口} Will Exit 2016-04-06 20:08:40.517 MutiThread[4927:1577194] <NSThread: 0x7fb719d552f0>{number = 3, name = 廣州售票窗口} Will Exit

  若是肯定兩個線程都是isCancelled狀態,能夠調用[NSThread exit]方法來終止線程。

Next

  接下來將更新GCD和NSOperation篇

相關文章
相關標籤/搜索