IOS——多線程

##一、基本概念ios

  • 什麼是進程: 進程是在系統運行的一個程序,每一個進程之間是獨立的,每一個進程均運行在其專有且受保護的內存空間內。git

  • 什麼是線程: 一個進程想要執行任務,必須得有線程(至少一個線程),線程是進程的基本執行單位,一個進程的全部任務都必須在線程中執行。github

  • 線程的串行: 一個線程中任務的執行是串行的,若是要在一個線程中執行多個任務,只能一個一個的按順序執行編程

##二、多線程安全

  • 什麼是多線程:服務器

    一個進程中能夠開啓多個線程,每一個線程能夠併發/並行執行不一樣的任務,多線程能夠提交程序的執行效率,好比同時執行任務ABC。多線程

  • 多線程原理:併發

    同一時間,CPU只能執行一個線程,只有一個線程正在執行,多線程併發執行,實際上是CPU快速的在多個線程之間切換,若是CPU的切換線程的時間足夠快,就會形成多線程併發執行的假象。框架

  • 多線程的優缺點:異步

優勢: 1.能適當的提升程序的執行效率 2.能適當的提升資源的利用率 缺點: 1.開啓線程會佔用必定的內存空間(主線程1M,子線程0.5M),若是開啓過多的線程就會佔用大量的內存空間,下降程序的性能。 2.線程越多,CPU在調度線程上的開銷就越大。

##三、主線程

一個IOS程序運行之後,默認會開啓一個線程,這個線程就被稱爲主線程或(UI線程)。主線程的主要做用是顯示/刷新UI界面,處理UI事件(點擊,滾動,拖拽等)。

IOS中的多線程:

iOS中有四種多線程編程的技術:

1.Pthread (基本不會使用,線程的生命週期由咱們⾃己管理)

2.NSThread(每一個Thread對象對應⼀一個線程)(使⽤用得⽐比較少,線程的⽣生命週期由咱們⾃己管理)

3.NSOperation(面向對象的線程技術)(基於gcd來實現,常常使⽤,⽣命週期由系統管理)

4.GCD(是基於C語⾔言的框架,能夠充分利用多核,是蘋果推薦使⽤的多線程技術)(常用,生命週期由系統管理))

以上這四種編程⽅方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡單,也是Apple最推薦使用的。可是就目前而言,iOS的開發者,須要瞭解三種多線程技術的基本使⽤用過程。由於不少 框架技術分別使用了不一樣多線程技術。

##四、NSThread

建立線程、執行下載任務,須要start手動開啓執行

NSThread *thread_A = [[NSThread alloc] initWithTarget:self selector:@selector(run_A) object:nil];
    thread_A.name = @"線程A";

啓動任務

[thread_A start];

阻塞線程,等待幾秒,是這個線程裏面的全部任務一塊兒阻塞

[NSThread sleepForTimeInterval:5];

阻塞到某個時間

[NSThread sleepUntilDate:[[NSDate date] dateByAddingTimeInterval:1000]];

方式二:指定任務直接執行不須要手動開啓 start

[NSThread detachNewThreadSelector:@selector(run_A) toTarget:self withObject:nil];

取消進程

[thread_A cancel];

獲取當前線程信息
+ (NSThread *)currentThread;
獲取主線程信息
+ (NSThread *)mainThread;

ps:若是是有關UI更新的操做,在其它線程中處理完以後要回到主線程進行改變

##五、GCD

(GCD)是Apple開發的⼀個多核編程的解決⽅方法。該⽅方法在Mac OS X 10.6首次推出,並隨後被引入到了iOS4.0中。GCD是⼀個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。

###5.1 任務和隊列

在GCD中,加入了兩個很是重要的概念:任務和隊列。

任務:

即操做,你想要幹什麼,說白了就是一段代碼,在 GCD 中就是一個 Block,因此添加任務十分方便。

任務有兩種執行方式: 同步執行 和 異步執行,

  • 同步(sync) 和 異步(async) 的主要區別在於會不會阻塞當前線程,直到 Block 中的任務執行完畢!

  • 若是是 同步(sync) 操做,它會阻塞當前線程並等待 Block 中的任務執行完畢,而後當前線程纔會繼續往下運行。

  • 若是是 異步(async)操做,當前線程會直接往下執行,它不會阻塞當前線程。

隊列:

  • 用於存聽任務。一共有兩種隊列, 串行隊列DISPATCH_QUEUE_SERIAL 和 並行隊列DISPATCH_QUEUE_CONCURRENT.

放到串行隊列的任務,GCD 會 FIFO(先進先出) 地取出來一個,執行一個,而後取下一個,這樣一個一個的執行。

放到並行隊列的任務,GCD 也會 FIFO的取出來,但不一樣的是,它取出來一個就會放到別的線程,而後再取出來一個又放到另外一個的線程。這樣因爲取的動做很快,忽略不計,看起來,全部的任務都是一塊兒執行的。不過須要注意,GCD 會根據系統資源控制並行的數量,因此若是任務不少,它並不會讓全部任務同時執行。

並行隊列 中的任務根據同步或異步有不一樣的執行方式。請看下錶:

主隊列 全局隊列 自定義串行隊列 自定義並行隊列
同步 死鎖(一直卡主) 在主線程中順序執行 在主線程中順序執行 在主線程中順序執行
異步 在主線程中順序執行 在其它線程中同時執行 在其它線程中順序執行 在其它線程中同時執行

###5.2 建立隊列

  • 主隊列

這是一個特殊的串行隊列。它用於刷新UI,任何須要刷新UI的工做都要在主隊列執行,因此通常耗時的任務都要放到別的線程執行。

同步任務:dispatch_sync,會阻塞後面的任務,必需當前任務完成後才能執行下一個

主隊列中不能執行同步操做,否則會死鎖

異步執行:dispatch_async,不會阻塞後面的任務,任務會立刻返回下一個任務不須要等待,當這個任務完成後會通知主線程。 dispatch_get_main_queue() 獲取主隊列,主隊列中的任務都會在主線程中執行, 是一個串行隊列

dispatch_async(dispatch_get_main_queue(), ^{
       
        [self run_D];
    });
  • 全局隊列

dispatch_get_global_queue() 全局隊列,是一個並行隊列,能夠將隊列中的任務放到不一樣的線程中執行

任務執行的優先級
        DISPATCH_QUEUE_PRIORITY_HIGH 2  最高
        DISPATCH_QUEUE_PRIORITY_DEFAULT 0 中等(默認)
        DISPATCH_QUEUE_PRIORITY_LOW (-2) 低
        DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 後臺執行
        
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        [self run_A];
    });

若是在並行隊列中同步執行任務,那麼這些任務都會在主線程中按順序執行,也就沒有併發性了。

  • 自定義串行隊列

自定義的串行隊列中異步執行任務,隊列會把任務放到一個新的線程中按順序執行

dispatch_queue_t serialQueue = dispatch_queue_create("串行隊列", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(serialQueue, ^{
        
        [self run_A];
    });
  • 自定義的並行隊列

自定義的並行隊列中異步執行任務,隊列會把任務放到不一樣的線程中執行

ispatch_queue_t concurrentlQueue = dispatch_queue_create("並行隊列", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(concurrentlQueue, ^{
        
        [self run_A];
    });

將更新UI的操做放到主隊列中

dispatch_async(dispatch_get_main_queue(), ^{
        
        self.imagView_1.image = image;
    
    });

###5.3 隊列組

隊列組能夠將不少隊列添加到一個組裏,這樣作的好處是,當這個組裏全部的任務都執行完了,隊列組會經過一個方法通知咱們。

建立一個隊列組

dispatch_group_t group = dispatch_group_create();

建立隊列

dispatch_queue_t queue = dispatch_queue_create("並行隊列", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

在group中異步執行任務,block中調用局部變量須要加__block

__block UIImage *image_1;
   
    dispatch_group_async(group, queue, ^{
       
        image_1 = [self downloadImage_one];
    });

當group中的任務都執行完後,就會發送一個通知notify調用這個方法

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       
        self.imagView_1.image = image_1;        
    });

###5.4 線程安全

一次執行,block中的代碼只能被執行一次,是線程保護的(同時只能一個線程訪問)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self run_B];
    });

單例通常用這個 線程安全的單例建立

+ (instancetype)shareUser_GCD {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        user = [[self alloc] init];
    });
    
    return user;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    
    static User *user = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        user = [super allocWithZone:zone];
    });
    
    return user;
}

延遲執行

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self run_B];
    });

##六、NSOperation和NSOperationQueue

NSOperation 是蘋果公司對 GCD 的封裝,徹底面向對象,因此使用起來更好理解。 NSOperation 和 NSOperationQueue 分別對應 GCD 的 任務 和 隊列 。操做步驟也很好理解:

1.將要執行的任務封裝到一個 NSOperation 對象中。 2.將此任務添加到一個 NSOperationQueue 對象中。

直接執行會在主線程中順序執行

NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil];

啓動任務

[opertaion_1 start];

至少會有一個任務在主線程中執行,其餘任務會被放到其餘線程中執行

NSBlockOperation *opertion = [NSBlockOperation blockOperationWithBlock:^{
       
        [self run_A];
    }];
    
    [opertion addExecutionBlock:^{
        
        [self run_B];
    }];
    
    [opertion addExecutionBlock:^{
       
        [self run_C];
    }];
    
    [opertion start];

###6.1 NSInvocationOperation依賴關係

NSOperation 有一個很是實用的功能,那就是添加依賴。好比有 3 個任務:A: 從服務器上下載一張圖片,B:給這張圖片加個水印,C:把圖片返回給服務器。這時就能夠用到依賴

注意:

  • 不能添加相互依賴,會死鎖,好比 A依賴B,B依賴A。
  • 可使用 removeDependency 來解除依賴關係。
  • 能夠在不一樣的隊列之間依賴,反正就是這個依賴是添加到任務身上的,和隊列不要緊。
NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil];
    NSInvocationOperation *opertaion_2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_B) object:nil];
    NSInvocationOperation *opertaion_3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_C) object:nil];
    
    // 添加依賴,opertaion_1 ————》opertaion_2 ————》opertaion_3
    
//    [opertaion_2 addDependency:opertaion_1];
//    [opertaion_3 addDependency:opertaion_2];

在這裏就是opertaion_2會等opertaion_1執行完後再執行,後面以此類推

建立一個隊列,默認就是並行隊列

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

設置當前最大的執行數,能夠控制是並行還串行,爲1時就是串行

queue.maxConcurrentOperationCount = 1;

將任務添加到隊列中任務就自動執行了

[queue addOperation:opertaion_1];
    [queue addOperation:opertaion_2];
    [queue addOperation:opertaion_3];
        [queue addOperationWithBlock:^{
    
            [self run_D];
        }];

添加多個任務,而且能夠設置等待完成

[queue addOperations:@[opertaion_1,opertaion_2,opertaion_3] waitUntilFinished:YES];

取消全部的任務

[queue cancelAllOperations];

取消指定的任務

[opertaion_1 cancel];

獲取當前的隊列

[NSOperationQueue currentQueue];

獲取主隊列

[NSOperationQueue mainQueue];

###6.2 其它方法

以上就是一些主要方法, 下面還有一些經常使用方法:

NSOperation

  • BOOL executing; //判斷任務是否正在執行
  • BOOL finished; //判斷任務是否完成
  • void (^completionBlock)(void); //用來設置完成後須要執行的操做
  • (void)cancel; //取消任務
  • (void)waitUntilFinished; //阻塞當前線程直到此任務執行完 畢

NSOperationQueue

  • NSUInteger operationCount; //獲取隊列的任務數
  • (void)cancelAllOperations; //取消隊列中全部的任務
  • (void)waitUntilAllOperationsAreFinished; //阻塞當前線程直到此隊列中的全部任務執行完畢
  • [queue setSuspended:YES]; // 暫停queue
  • [queue setSuspended:NO]; // 繼續queue

##7 GCD和NSOperation的區別:

一、GCD的底層是用C來實現的,NSOperation底層從ios4開始也是⽤的GCD來實 現的;(底層實現)

二、在NSOperationQueue中,咱們能夠隨時取消已經設定要準備執⾏的任務(固然,已經開始的任務就⽆法阻⽌了),⽽GCD無法中止已經加入queue的block(實際上是有的,但須要許多複雜的代碼);(取消任務)

三、NSOperation可以⽅便地設置依賴關係,咱們可讓⼀個Operation依賴於另⼀個Operation,這樣的話儘管兩個Operation處於同⼀個並行隊列中,但前者會直到後者執行完畢後再執⾏;(依賴關係)

四、咱們能將KVO應⽤用在NSOperation中,能夠監聽一個Operation是否完成或取消,這樣子能⽐GCD更加有效地掌控咱們執⾏的後臺任務;(監放任務的執⾏狀況)

五、在NSOperation中,咱們可以設置NSOperation的priority優先級,可以使同⼀個並行隊列中的任務區分前後地執行,⽽在GCD中,咱們只能區分不一樣任務隊列的優先級,若是要區分block任務的優先級,也須要大量的複雜代碼;(優先級) 六、咱們可以對NSOperation進行繼承,在這之上添加成員變量與成員方法,提⾼整個代碼的復⽤度,這⽐簡單地將block任務排⼊執行隊列更有自由度,可以在其之上添加更多自定製的功能。(代碼複用)

Demo下載:

https://github.com/fuxinto/HfxDemo

相關文章
相關標籤/搜索