iOS 多線程

原文: 查看原文 , 原作者寫的非常詳細(xì), 本文摘取了個人認(rèn)為比較有用且易理解的部分, 碼一遍增強(qiáng)理解, 大家可以移步原文閱讀.

iOS 中可用的4套多線程方案, 并不都建議使用, 視情況而定
  • Pthreads
  • NSThread
  • GCD
  • NSOperation & NSOperationQueue

Pthreads

可移植性很強(qiáng), 但由于基于 c語言框架, 所以并不建議使用.

#import <pthread.h>

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    pthread_t thread;
    //創(chuàng)建一個線程并自動執(zhí)行
    pthread_create(&thread, NULL, start, NULL);
}

void *start(void *data) {
    NSLog(@"%@", [NSThread currentThread]);

    return NULL;
}

需要手動管理生命周期


NSThread

經(jīng)過蘋果封裝, 可以直接操控線程對象. 通過[NSThread currentThread]獲取當(dāng)前線程類及各種屬性.

  • 先創(chuàng)建線程類 再啟動
  // 創(chuàng)建
  NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];

  // 啟動
  [thread start];
  • 創(chuàng)建并自動啟動
  [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
  • 使用 NSObject 方法創(chuàng)建并自動啟動
  [self performSelectorInBackground:@selector(run:) withObject:nil];
  • 判斷某個線程狀態(tài)的屬性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
  • 設(shè)置和獲取線程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
  • 獲取線程信息
//獲取當(dāng)前線程信息
+ (NSThread *)currentThread;

//獲取主線程信息
+ (NSThread *)mainThread;
  • 當(dāng)前線程暫停
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;

不夠優(yōu)雅


GCD - 大調(diào)度中心!

自動管理生命周期 , 雖然使用 c語言 但是由于使用了 Block, 所以用起來非常方便.

使用 GCD 只有兩個步驟:
1. 定制任務(wù)
2. 確定想做的事

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block) 將右邊的任務(wù)提交給左邊的隊列執(zhí)行.

同步( sync )異步( async )最大的區(qū)別在于會不會阻塞當(dāng)前線程, 直到 Block 中的任務(wù)執(zhí)行完畢.

同步( sync ) 它會阻塞當(dāng)前線程并等待 Block 中的任務(wù)執(zhí)行完畢,然后當(dāng)前線程才會繼續(xù)往下運(yùn)行。
異步( async ) 當(dāng)前線程會直接往下執(zhí)行,它不會阻塞當(dāng)前線程。

串行隊列 放到串行隊列的任務(wù),GCD 會 FIFO(先進(jìn)先出) 地取出來一個,執(zhí)行一個,然后取下一個,這樣一個一個的執(zhí)行。
并行隊列 放到并行隊列的任務(wù),GCD 也會 FIFO的取出來,但不同的是,它取出來一個就會放到別的線程,然后再取出來一個又放到另一個的線程。這樣由于取的動作很快,忽略不計,看起來,所有的任務(wù)都是一起執(zhí)行的。不過需要注意,GCD 會根據(jù)系統(tǒng)資源控制并行的數(shù)量,所以如果任務(wù)很多,它并不會讓所有任務(wù)同時執(zhí)行。

注: 無論是串行隊列還是并行隊列 , 都遵守先進(jìn)先出 FIFO 原則 , 只是并行隊列允許同時執(zhí)行多個任務(wù).

|同步執(zhí)行 | 異步執(zhí)行
---|--------|-------
串行隊列 | 當(dāng)前線程,一個一個執(zhí)行 | 其他線程,一個一個執(zhí)行
并行隊列 | 當(dāng)前線程,一個一個執(zhí)行 | 開很多線程,一起執(zhí)行

創(chuàng)建隊列

  • 主隊列 : 用于刷新 UI,任何需要刷新 UI 的工作都要在主隊列執(zhí)行,所以一般耗時的任務(wù)都要放到別的線程執(zhí)行。
  dispatch_queue_t queue = ispatch_get_main_queue();
  • 自己創(chuàng)建隊列 : 自己可以創(chuàng)建 串行隊列, 也可以創(chuàng)建 并行隊列。
    • 第一個參數(shù)是標(biāo)識符,用于 DEBUG 的時候標(biāo)識唯一的隊列,可以為空。
    • 第二個參數(shù)用來表示創(chuàng)建的隊列是串行的還是并行的,傳入 DISPATCH_QUEUE_SERIAL 或 NULL 表示創(chuàng)建串行隊列。傳入 DISPATCH_QUEUE_CONCURRENT 表示創(chuàng)建并行隊列。
  //串行隊列
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
  //并行隊列
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
  • 全局并行隊列 : 只要是并行任務(wù)一般都加入到這個隊列。這是系統(tǒng)提供的一個并發(fā)隊列。
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

創(chuàng)建任務(wù)

  • 同步任務(wù)( sync ): 會阻塞當(dāng)前線程
  dispatch_sync(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });
  • 異步任務(wù)( async ): 不會阻塞
dispatch_async(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });

NSOperation和NSOperationQueue

NSOperation 是蘋果公司對 GCD 的封裝,完全面向?qū)ο?,所以使用起來更好理解?大家可以看到 NSOperationNSOperationQueue 分別對應(yīng) GCD 的 任務(wù)隊列 。

添加任務(wù)

NSOperation 只是一個抽象類,所以不能封裝任務(wù)。但它有 2 個子類用于封裝任務(wù)。分別是:NSInvocationOperationNSBlockOperation 。創(chuàng)建一個 Operation 后,需要調(diào)用 start 方法來啟動任務(wù),它會 默認(rèn)在當(dāng)前隊列同步執(zhí)行。當(dāng)然你也可以在中途取消一個任務(wù),只需要調(diào)用其 cancel 方法即可。

  • NSInvocationOperation : 傳入一個方法
//1.創(chuàng)建NSInvocationOperation對象
  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

  //2.開始執(zhí)行
  [operation start];
  • NSBlockOperation
  //1.創(chuàng)建NSBlockOperation對象
  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"%@", [NSThread currentThread]);
  }];

  //2.開始任務(wù)
  [operation start];

默認(rèn)會在當(dāng)前線程執(zhí)行。但是 NSBlockOperation 還有一個方法:addExecutionBlock: ,通過這個方法可以給 Operation 添加多個執(zhí)行 Block。這樣 Operation 中的任務(wù) 會并發(fā)執(zhí)行,它會在主線程和其它的多個線程 執(zhí)行這些任務(wù)

 //1.創(chuàng)建NSBlockOperation對象
      NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
          NSLog(@"%@", [NSThread currentThread]);
      }];

      //添加多個Block
      for (NSInteger i = 0; i < 5; i++) {
          [operation addExecutionBlock:^{
              NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
          }];
      }

      //2.開始任務(wù)
      [operation start];

NSOperationQueue 有一個參數(shù) maxConcurrentOperationCount 最大并發(fā)數(shù),用來設(shè)置最多可以讓多少個任務(wù)同時執(zhí)行。當(dāng)你把它設(shè)置為 1 的時候,就是串行.

NSOperationQueue 還有一個添加任務(wù)的方法,- (void)addOperationWithBlock:(void (^)(void))block; ,這是不是和 GCD 差不多?這樣就可以添加一個任務(wù)到隊列中了,十分方便。

//1.任務(wù)一:下載圖片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下載圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//2.任務(wù)二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"打水印   - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//3.任務(wù)三:上傳圖片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"上傳圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//4.設(shè)置依賴
[operation2 addDependency:operation1];      //任務(wù)二依賴任務(wù)一
[operation3 addDependency:operation2];      //任務(wù)三依賴任務(wù)二

//5.創(chuàng)建隊列并加入任務(wù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
  • 注意:不能添加相互依賴,會死鎖,比如 A依賴B,B依賴A。
  • 可以使用 removeDependency 來解除依賴關(guān)系。
  • 可以在不同的隊列之間依賴,反正就是這個依賴是添加到任務(wù)身上的,和隊列沒關(guān)系。

延遲執(zhí)行

  • perform
// 3秒后自動調(diào)用self的run:方法,并且傳遞參數(shù):@"abc"
  [self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];
  • GCD
// 創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 設(shè)置延時,單位秒
double delay = 3; 

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{
    // 3秒后需要執(zhí)行的任務(wù)
});
  • NSTimer
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];

從其他線程回到主線程

  • NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
  • GCD
dispatch_async(dispatch_get_main_queue(), ^{

});
  • NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{

}];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 多線程 在iOS開發(fā)中為提高程序的運(yùn)行效率會將比較耗時的操作放在子線程中執(zhí)行,iOS系統(tǒng)進(jìn)程默認(rèn)啟動一個主線程,用...
    郭豪豪閱讀 2,721評論 0 4
  • 在這篇文章中,我將為你整理一下 iOS 開發(fā)中幾種多線程方案,以及其使用方法和注意事項。當(dāng)然也會給出幾種多線程的案...
    張戰(zhàn)威ican閱讀 699評論 0 0
  • 學(xué)習(xí)多線程,轉(zhuǎn)載兩篇大神的帖子,留著以后回顧!第一篇:關(guān)于iOS多線程,你看我就夠了 第二篇:GCD使用經(jīng)驗(yàn)與技巧...
    John_LS閱讀 740評論 0 3
  • 多線程基本概念 單核CPU,同一時間cpu只能處理1個線程,只有1個線程在執(zhí)行 。多線程同時執(zhí)行:是CPU快速的在...
    WeiHing閱讀 786評論 1 5
  • 在這篇文章中,我將為你整理一下 iOS 開發(fā)中幾種多線程方案,以及其使用方法和注意事項。當(dāng)然也會給出幾種多線程的案...
    伯恩的遺產(chǎn)閱讀 275,635評論 251 2,328

友情鏈接更多精彩內(nèi)容