iOS_多線程四:NSThread + 總結

 

一、NSThread

是iOS中輕量級得多線程,一個NSThread對象對應一條線程

1、一些類方法

[NSThread mainThread]; // 獲取主線程
[NSThread currentThread]; // 獲取當前線程
// 阻塞當前線程,設置休眠時間,兩種方式實現:
[NSThread sleepForTimeInterval:3];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
[NSThread exit]; // 立即終止主線程之外的所有線程(包括正在執行任務的)
// 注意:需要在掌控所有線程狀態的情況下調用此方法,否則可能會導致內存問題。
//   threadPriority相關的都已禁用,改用qualityOfService(枚舉)代替
[NSThread threadPriority]; // 獲取當前線程優先級
[NSThread setThreadPriority:0.5]; // 設置優先級:0.0~1.0;1.0優先級最高

2、創建方式

(1)、alloc init創建,但是需要手動開啓

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(network:) object:@{@"name":@"moxiaohui"}];
[thread start];

[thread setName:@"moxiaoyan"]; // 線程名稱
thread.qualityOfService = NSQualityOfServiceUserInteractive;
//  NSQualityOfServiceUserInteractive = 0x21, // 最高優先級, 用於處理 UI 相關的任務
//  NSQualityOfServiceUserInitiated = 0x19, // 次高優先級, 用於執行需要立即返回的任務
//  NSQualityOfServiceUtility = 0x11, // 普通優先級,主要用於不需要立即返回的任務
//  NSQualityOfServiceBackground = 0x09, // 後臺優先級,用於處理一些用戶不會感知的任務
//  NSQualityOfServiceDefault = -1 // 默認優先級,當沒有設置優先級的時候,線程默認優先級
thread.stackSize = 8192; // 更改堆棧的大小: 必須 是4KB(1024)的倍數 && 啓動線程之前設置 (創建線程是會有開銷的)
NSUInteger size = thread.stackSize / 1024; // 所佔內存大小
[thread cancel]; // 不會馬上退出,做了需要退出的標記
[thread isMainThread];  // 是否是主線程
[thread isFinished];  // 是否已經完成
[thread isCancelled]; // 是否已經取消
[thread isExecuting]; // 是否正在執行中


- (void)network:(NSDictionary *)info {
  NSLog(@"執行 %@", [NSThread currentThread]);
  NSLog(@"info: %@", info);
  sleep(2);
  NSLog(@"完成");
}

(2)、初始化一個子線程,特點:自動開啓,是類方法

@autoreleasepool {
  [NSThread detachNewThreadSelector:@selector(network:) toTarget:self withObject:@{@"name":@"moxiaohui"}];
}

(3)、隱式創建

// 子線程中執行:(耗時操作)
[self performSelectorInBackground:@selector(network:) withObject:@{@"name":@"moxiaohui"}];
// 主線程中執行:(執行更新UI之類得操作)
[self performSelectorOnMainThread:@selector(complete) withObject:nil waitUntilDone:YES];
// 指定線程中執行
[self performSelector:@selector(complete) onThread:[NSThread mainThread] withObject:@{@"name":@"moxiaohui"} waitUntilDone:YES];
// 當前線程中執行
[self performSelector:@selector(network:) withObject:@{@"name":@"moxiaohui"}];
[self performSelector:@selector(network:) withObject:@{@"name":@"moxiaoyan"} withObject:@{@"name":@"moxiaohui"}];
[self performSelector:@selector(afterDelay:) withObject:@{@"name":@"moxiaoyan"} afterDelay:5]; // 5s後執行
// cancel 某一個方法
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(afterDelay:) object:@{@"name":@"moxiaoyan"}];
// cancel 當前對象所有perform方法
[NSObject cancelPreviousPerformRequestsWithTarget:self];


- (void)afterDelay:(NSDictionary *)info {
  NSLog(@"afterDelay info:%@", info);
}

- (void)complete {
  NSLog(@"Update UI");
}

二、需要手動加鎖(線程同步)(缺點)

很多大神舉的例子哈,我借鑑一下:多窗口買票的情況,不加鎖,數據會錯亂

#pragma mark - 多窗口買票
- (void)MultiWindowTicket {
  self.totalTicketCount = 20;
  _thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
  _thread1.name = @"窗口1";
  _thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
  _thread2.name = @"窗口2";
  _thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
  _thread3.name = @"窗口3";
  [_thread1 start];
  [_thread2 start];
  [_thread3 start];
}

- (void)saleTicket {
  while (YES) { // 模擬還有票會持續`-1`的操作
//    @synchronized (self) { // 互斥鎖:swift 用 objc_sync_enter(self) 和 objc_sync_exit(self)
      if (self.totalTicketCount > 0) {
        self.totalTicketCount--;
        NSLog(@"買了一張,還剩:%ld %@", (long)self.totalTicketCount, [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.5];
      } else {
        NSLog(@"票買完了");
        break;
      }
//    }
  }
}
// 執行結果:
// 買了一張,還剩:7 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 買了一張,還剩:5 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 買了一張,還剩:6 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 買了一張,還剩:3 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 買了一張,還剩:4 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 買了一張,還剩:4 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 買了一張,還剩:2 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 買了一張,還剩:2 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 買了一張,還剩:1 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 買了一張,還剩:0 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}
// 票買完了 <NSThread: 0x600003a41a80>{number = 9, name = 窗口3}
// 票買完了 <NSThread: 0x600003a41a00>{number = 7, name = 窗口1}
// 票買完了 <NSThread: 0x600003a41a40>{number = 8, name = 窗口2}

從執行結果可以看的出來,會有多個窗口在售出一張票後,結算的剩餘票數是一樣的(也就是說他們把同一張票賣給了多個人)

所以NSThread是線程不安全的,需要程序猿自己手動加鎖,保持線程同步!!!

 

三、多線程總結

GCD、NSOperation、NSThread的優缺點

三種多線程比較
  GCD NSOperation NSThread
實現 C OC OC(Pthread基於C實現)
線程安全

安全

安全

不安全(需要手動加鎖,導致性能低!!!)

生命週期

自動管理

自動管理

程序猿管理

輕量級別

性能
其他 跟Block結合代碼簡潔

多了些實用功能

(如:順序設置、未執行前取消...)

簡單易用(無需做過多設置),

更直觀操作線程對象

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章