iOS多線程學(xué)習(xí)筆記

多線程的基本知識

先補(bǔ)一發(fā)基礎(chǔ)知識

什么是線程

線程,有時(shí)被稱為輕量級進(jìn)程(Lightweight Process,LWP),是程序執(zhí)行流的最小單元。一個(gè)標(biāo)準(zhǔn)的線程由線程ID,當(dāng)前指令指針(PC),寄存器集合和堆棧組成。

線程是程序中一個(gè)單一的順序控制流程。進(jìn)程內(nèi)一個(gè)相對獨(dú)立的、可調(diào)度的執(zhí)行單元,是系統(tǒng)獨(dú)立調(diào)度和分派CPU的基本單位指運(yùn)行中的程序的調(diào)度單位。在單個(gè)程序中同時(shí)運(yùn)行多個(gè)線程完成不同的工作,稱為多線程

線程與進(jìn)程的關(guān)系

線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)兒在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。一個(gè)線程可以創(chuàng)建和撤消另一個(gè)線程,同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。

線程與進(jìn)程的區(qū)別

進(jìn)程

進(jìn)程是資源分配的基本單位。每個(gè)進(jìn)程擁有自己獨(dú)立的進(jìn)程控制塊(PCB,Process Control Block),不同的進(jìn)程擁有不同的虛擬地址空間。

進(jìn)程間通信,常用方法為:

  • 管道(pipe)
  • 消息隊(duì)列(message queue)
  • 信號(sinal)
  • 信號量(semophore)
  • 共用內(nèi)存(shared memory)
  • 套接字(socket)

線程

線程是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位。線程只由相關(guān)堆棧(系統(tǒng)?;蛴脩魲#┘拇嫫骱途€程控制表(TCB,Thread Control Table)組成,同一進(jìn)程內(nèi)的不同線程共享同一地址空間。

線程間通信,常用方法為:

  • 鎖(lock):互斥鎖、條件變量、讀寫鎖等
  • 信號量(semophore)
  • 信號(signal):用于線程同步

線程的狀態(tài)

線程的狀態(tài)

iOS中的多線程技術(shù)

pthread

簡介

POSIX線程(英語:POSIX Threads,常被縮寫為Pthreads)是POSIX的線程標(biāo)準(zhǔn),定義了創(chuàng)建和操縱線程的一套API。

實(shí)現(xiàn)POSIX 線程標(biāo)準(zhǔn)的庫常被稱作Pthreads,一般用于Unix-like POSIX 系統(tǒng),如Linux、Solaris。但是Microsoft Windows上的實(shí)現(xiàn)也存在,例如直接使用Windows API實(shí)現(xiàn)的第三方庫pthreads-w32;而利用Windows的SFU/SUA子系統(tǒng),則可以使用微軟提供的一部分原生POSIX API。

使用

  1. 包含頭文件#import <pthread.h>

  2. 創(chuàng)建線程,并且開啟線程執(zhí)行任務(wù)

    // 創(chuàng)建線程——定義一個(gè)pthread_t類型變量
    pthread_t thread;
    // 開啟線程——執(zhí)行任務(wù)
    pthread_create(&thread, NULL, run, NULL);
    
    // 新線程調(diào)用方法,里邊為需要執(zhí)行的任務(wù)
    void * run(void *param) {
        NSLog(@"%@", [NSThread currentThread]);
        return NULL;
    }
    

NSThread

簡介

NSThread是蘋果官方提供的,基于c語言封裝,使用起來比pthread更加面向?qū)ο?,簡單易用,可以直接操作線程對象。不過也需要需要我們自己管理線程的生命周期(主要是創(chuàng)建),我們在開發(fā)的過程中偶爾使用NSThread。比如我們會(huì)經(jīng)常調(diào)用[NSThread currentThread]來顯示當(dāng)前的進(jìn)程信息。

使用

啟動(dòng)
  • 創(chuàng)建線程,并手動(dòng)啟動(dòng)

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

    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
    
  • 使用NSObject的方法創(chuàng)建并啟動(dòng)

    [self performSelectorInBackground:@selector(run:) withObject:nil];
    
其他方法
//取消線程
- (void)cancel;

//啟動(dòng)線程
- (void)start;

//強(qiáng)制停止線程
+ (void)exit;

//判斷某個(gè)線程的狀態(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)前線程暫停(阻塞)一段時(shí)間,或者暫停到某個(gè)時(shí)刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;

GCD

簡介

Grand Central Dispatch (GCD) 是Apple開發(fā)的一個(gè)多核編程的較新的解決方法。它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)。它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。

使用

啟動(dòng)
  1. 創(chuàng)建隊(duì)列

    使用dispatch_queue_create來創(chuàng)建隊(duì)列

    • 第一個(gè)參數(shù)表示隊(duì)列的唯一標(biāo)識符,用于DEBUG,可為空
    • 第二個(gè)參數(shù)用來識別是串行隊(duì)列還是并發(fā)隊(duì)列。DISPATCH_QUEUE_SERIAL表示串行隊(duì)列,DISPATCH_QUEUE_CONCURRENT表示并發(fā)隊(duì)列。
      • 這里的同步(sync)異步(async)的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程,直到Block 中的任務(wù)執(zhí)行完畢

    使用dispatch_get_xxx來獲取隊(duì)列

    • dispatch_get_global_queue會(huì)獲取一個(gè)全局隊(duì)列

    • dispatch_get_main_queue 會(huì)獲取主隊(duì)列,也就是UI隊(duì)列

      // 串行隊(duì)列的創(chuàng)建方法
      dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
      // 并發(fā)隊(duì)列的創(chuàng)建方法
      dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
      
  2. 創(chuàng)建任務(wù)

    // 同步執(zhí)行任務(wù)創(chuàng)建方法
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);    // 這里放任務(wù)代碼
    });
    // 異步執(zhí)行任務(wù)創(chuàng)建方法
    dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);    // 這里放任務(wù)代碼
    });
    
管理
  1. 柵欄方法dispatch_barrier_async

    第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來,當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄。

    dispatch_async(queue, ^{...});
    dispatch_async(queue, ^{...});
    
    // 當(dāng)上面兩個(gè)異步任務(wù)執(zhí)行完后才執(zhí)行下面的異步任務(wù)
    dispatch_barrier_async(queue, ^{...});
    
    dispatch_async(queue, ^{...});
    dispatch_async(queue, ^{...});
    
  2. 延時(shí)執(zhí)行方法 dispatch_after

    當(dāng)我們需要延遲執(zhí)行一段代碼時(shí),就需要用到GCD的dispatch_after方法。

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       // 2秒后異步執(zhí)行這里的代碼
       // ...
    });
    
  3. 一次性代碼(只執(zhí)行一次)dispatch_once

    在創(chuàng)建單例、或者有整個(gè)程序運(yùn)行過程中只執(zhí)行一次的代碼時(shí),我們就用到了GCD的dispatch_once方法。使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次。

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
    });
    
  4. 快速迭代方法 dispatch_apply

    GCD給我們提供了快速迭代的方法dispatch_apply,使我們可以同時(shí)遍歷。比如說遍歷0~5這6個(gè)數(shù)字,for循環(huán)的做法是每次取出一個(gè)元素,逐個(gè)遍歷。dispatch_apply可以同時(shí)遍歷多個(gè)數(shù)字。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd------%@",index, [NSThread currentThread]);
    });
    
  5. 隊(duì)列組dispatch_group

    分別異步執(zhí)行2個(gè)耗時(shí)操作,然后當(dāng)2個(gè)耗時(shí)操作都執(zhí)行完畢后再回到主線程執(zhí)行操作。這時(shí)候我們可以用到GCD的隊(duì)列組。

    • 我們可以先把任務(wù)放到隊(duì)列中,然后將隊(duì)列放入隊(duì)列組中。
    • 調(diào)用隊(duì)列組的dispatch_group_notify回到主線程執(zhí)行操作。
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 執(zhí)行1個(gè)耗時(shí)的異步操作
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 執(zhí)行1個(gè)耗時(shí)的異步操作
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的異步操作都執(zhí)行完畢后,回到主線程...
    });
    

NSOperation

簡介

NSOperation是蘋果提供給我們的一套多線程解決方案。實(shí)際上NSOperation是基于GCD更高一層的封裝,但是比GCD更簡單易用、代碼可讀性也更高。

使用

啟動(dòng)
  1. 創(chuàng)建任務(wù)

    不使用NSOperationQueue,單獨(dú)使用NSOperation的情況下,系統(tǒng)同步執(zhí)行操作

    • 使用子類NSInvocationOperation

      NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
      
      [op start];
      
      - (void)run {
        // do sth
      }
      
    • 使用子類NSBlockOperation

      NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
         // do sth
      }];
      
      // 添加額外的任務(wù)(在子線程執(zhí)行)
      [op addExecutionBlock:^{
         // do sth
      }];
      [op addExecutionBlock:^{
         // do sth
      }];
      [op addExecutionBlock:^{
         // do sth
      }];
      
      [op start];
      
    • 定義繼承自NSOperation的子類,通過實(shí)現(xiàn)內(nèi)部相應(yīng)的方法來封裝任務(wù)

      • 定義一個(gè)繼承自NSOperation的子類,重寫main方法
  2. 創(chuàng)建隊(duì)列

    • 主隊(duì)列

      • 凡是添加到主隊(duì)列中的任務(wù)(NSOperation),都會(huì)放到主線程中執(zhí)行

        NSOperationQueue *queue = [NSOperationQueue mainQueue];
        
    • 其他隊(duì)列(非主隊(duì)列)

      • 添加到這種隊(duì)列中的任務(wù)(NSOperation),就會(huì)自動(dòng)放到子線程中執(zhí)行

      • 同時(shí)包含了:串行、并發(fā)功能

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
  3. 把任務(wù)加入隊(duì)列

    1. (void)addOperation:(NSOperation *)operation;

      // 1.創(chuàng)建隊(duì)列
      NSOperationQueue *queue = [[NSOperationQueue alloc] init];
      
      // 2. 創(chuàng)建操作  
      // 創(chuàng)建NSInvocationOperation    
      NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];    
      // 創(chuàng)建NSBlockOperation    
      NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
          // do sth
      }];
      
      // 3. 添加操作到隊(duì)列中:addOperation:   
      [queue addOperation:op1]; // [op1 start]    
      [queue addOperation:op2]; // [op2 start]
      
    2. (void)addOperationWithBlock:(void (^)(void))block;

      // 1. 創(chuàng)建隊(duì)列
      NSOperationQueue *queue = [[NSOperationQueue alloc] init];
      
      // 2. 添加操作到隊(duì)列中:addOperationWithBlock:
      [queue addOperationWithBlock:^{
          for (int i = 0; i < 2; ++i) {
              NSLog(@"-----%@", [NSThread currentThread]);
          }
      }];
      
管理
  1. 控制并發(fā)和串行

    通過NSOperationQueue最大并發(fā)數(shù):maxConcurrentOperationCount,實(shí)現(xiàn)串行和并發(fā)的控制

    • maxConcurrentOperationCount默認(rèn)情況下為-1,表示不進(jìn)行限制,默認(rèn)為并發(fā)執(zhí)行。
    • 當(dāng)maxConcurrentOperationCount為1時(shí),進(jìn)行串行執(zhí)行。
    • 當(dāng)maxConcurrentOperationCount大于1時(shí),進(jìn)行并發(fā)執(zhí)行,當(dāng)然這個(gè)值不應(yīng)超過系統(tǒng)限制,即使自己設(shè)置一個(gè)很大的值,系統(tǒng)也會(huì)自動(dòng)調(diào)整。
    // 創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 設(shè)置最大并發(fā)操作數(shù)
    //    queue.maxConcurrentOperationCount = 2;
    queue.maxConcurrentOperationCount = 1; // 就變成了串行隊(duì)列
    
  2. 操作依賴

    NSOperationNSOperationQueue最吸引人的地方是它能添加操作之間的依賴關(guān)系。比如說有A、B兩個(gè)操作,其中A執(zhí)行完操作,B才能執(zhí)行操作,那么就需要讓B依賴于A。

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1-----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2-----%@", [NSThread  currentThread]);
    }];
    
    [op2 addDependency:op1];    // 讓op2 依賴于 op1,則先執(zhí)行op1,在執(zhí)行op2
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    
  3. 其他方法

    // 提供的方法,可取消單個(gè)操作
    - (void)cancel; NSOperation
      
    // NSOperationQueue提供的方法,可以取消隊(duì)列的所有操作
    - (void)cancelAllOperations; 
    
    // 可設(shè)置任務(wù)的暫停和恢復(fù),YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列
    - (void)setSuspended:(BOOL)bool; 
    
    // 判斷暫停狀態(tài)
    - (BOOL)isSuspended; 
    
    • 這里的暫停和取消并不代表可以將當(dāng)前的操作立即取消,而是當(dāng)當(dāng)前的操作執(zhí)行完畢之后不再執(zhí)行新的操作。
    • 暫停和取消的區(qū)別就在于:暫停操作之后還可以恢復(fù)操作,繼續(xù)向下執(zhí)行;而取消操作之后,所有的操作就清空了,無法再接著執(zhí)行剩下的操作。

對比

  • pthread
    • 優(yōu)點(diǎn)
      • 接近底層
    • 缺點(diǎn)
      • 難于操作,需要自己管理線程的生命周期
  • NSThread
    • 優(yōu)點(diǎn)
      • pthread更加的面向?qū)ο?/li>
      • 輕量級最低,相對簡單
    • 缺點(diǎn)
      • 需要自己管理線程的生命周期,如生命周期、線程同步、睡眠等
  • GCD
    • 優(yōu)點(diǎn)
      • 可用于多核的并行運(yùn)算
      • 自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)
      • 自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
      • 只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
    • 缺點(diǎn)
      • 不可取消
  • NSOperation
    • 優(yōu)點(diǎn)
      • 比GCD簡單易用,代碼可讀性更高
      • 自帶線程周期管理,操作上可更注重自己邏輯
      • 能添加操作之間的依賴關(guān)系
      • 可以指定最大并發(fā)數(shù)
      • 可取消
    • 缺點(diǎn)
      • 沒有GCD簡潔
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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