iOS 多線程--GCD

前言:本文主要是關于GCD和NSOperation的具體使用,以及關于線程安全的實現.

線程以及進程的基本概念就不贅述了.

GCD

首先需要明確幾個基本的概念:

  • 串行(同步)執行與并發(異步)執行
  • 同步函數與異步函數
  • 串行隊列與并發隊列
串行執行與并發執行
 這里的串行執行與并發執行決定的是任務的執行方式
  • 串行執行: 如果我沒有執行完,那么后面的將永遠無法執行
  • 并發執行:我無所謂,我沒執行完后面的也可以執行
同步函數與異步函數
  • 同步函數(disaptach_sync):
    特點:只能在當前線程中執行任務,不具備開啟新線程的能力
    執行方式:同步
  • 異步函數(disaptach_async):
    特點:可以在新的線程中執行任務,具備開啟新線程的能力
    執行方式:異步
    注意:任務從隊列中取出來時時有有序的,遵循FIFO原則,先進先出,但是任務在線程中執行是無序的,并且如果遇到卡頓 阻塞時 可以先繼續往下執行,等前面的任務執行完畢之后再回頭執行之前遇到卡頓沒有執行的任務.
串行隊列與并發隊列

說到隊列就得提下GCD中的兩個核心概念: 任務與隊列
任務: 執行什么操作 隊列: 用來存放任務
隊列分兩種就是我們要說的串行隊列與并發隊列

  • Serial Diapatch Queue 串行隊列

當任務相互依賴,具有明顯的先后順序的時候,使用串行隊列是一個不錯的選擇 創建一個串行隊列:
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);

第一個參數為隊列名,第二個參數為隊列類型,當然,第二個參數如果寫NULL,創建出來的也是一個串行隊列。

通過異步函數來執行

dispatch_async(serialDispatchQueue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
});

dispatch_async(serialDispatchQueue, ^{
    sleep(2);
    NSLog(@"2---%@",[NSThread currentThread]);
});

dispatch_async(serialDispatchQueue, ^{
    sleep(1);
    NSLog(@"3----%@",[NSThread currentThread]);
});
控制臺log的結果

2017-05-24 15:57:02.842495 多線程[14286:6618708] 1---<NSThread: 0x170072ec0>{number = 3, name = (null)}
2017-05-24 15:57:04.847125 多線程[14286:6618708] 2---<NSThread: 0x170072ec0>{number = 3, name = (null)}
2017-05-24 15:57:05.852685 多線程[14286:6618708] 3----<NSThread: 0x170072ec0>{number = 3, name = (null)}

可以看到三個任務是在開啟的子線程中串行輸出,相互彼此依賴,串行執行

- Concurrent Diapatch Queue 并發隊列
與串行隊列剛好相反,他不會存在任務間的相互依賴。

創建一個并發隊列:
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
比較2個隊列的創建,我們發現只有第二個參數從DISPATCH_QUEUE_SERIAL變成了對應的DISPATCH_QUEUE_CONCURRENT,其他完全一樣。


    用同一段代碼,換一種隊列我們來比較一下效果:
    dispatch_queue_t concurrentDispatchQueue      =          dispatch_queue_create("com.test.queue",    DISPATCH_QUEUE_CONCURRENT);
   dispatch_async(concurrentDispatchQueue, ^{
    NSLog(@"1---%@",[NSThread currentThread]);
     });

   dispatch_async(concurrentDispatchQueue, ^{
    sleep(2);
    NSLog(@"2---%@",[NSThread currentThread]);
   });

    dispatch_async(concurrentDispatchQueue, ^{
    sleep(1);
    NSLog(@"3----%@",[NSThread currentThread]);
    });


2017-05-24 17:10:46.003170 多線程[14299:6629156] 1---<NSThread: 0x170075300>{number = 3, name = (null)}
2017-05-24 17:10:47.005762 多線程[14299:6629157] 3----<NSThread: 0x174061cc0>{number = 4, name = (null)}
2017-05-24 17:10:48.007882 多線程[14299:6629156] 2---<NSThread: 0x170075300>{number = 3, name = (null)}

我們發現,log的輸出在3個不同編號的線程中進行,而且相互不依賴,不阻塞。

- Global Queue & Main Queue

這是系統為我們準備的2個隊列:

Global Queue 其實就是系統創建的Concurrent Diapatch Queue
Main Queue   其實就是系統創建的位于主線程的Serial Diapatch Queue
通常情況我們會把這2個隊列放在一起使用,也是我們最常用的開異步線程-執行異步任務-回主線程的一種方式:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"異步線程");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"異步主線程");
});
});

通過上面的代碼我們發現了2個點:

- dispatch_get_global_queue存在優先級,沒錯,他一共有4個優先級:

define DISPATCH_QUEUE_PRIORITY_HIGH 2

define DISPATCH_QUEUE_PRIORITY_DEFAULT 0

define DISPATCH_QUEUE_PRIORITY_LOW (-2)

define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSLog(@"4");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSLog(@"3");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"1");
});

在指定優先級之后,同一個隊列會按照這個優先級執行,打印的順序為1、2、3、4,當然這不是串行隊列,所以不存在絕對回調先后。

異步主線程

在日常工作中,除了在其他線程返回主線程的時候需要用這個方法,還有一些時候我們在主線程中直接調用異步主線程,這是利用dispatch_async的特性:block中的任務會放在主線程本次runloop之后返回。這樣,有些存在先后順序的問題就可以得到解決了。

####關于線程與隊列,以及任務的執行方式總結
![046EB302-B057-4D4D-9919-180BA087F1A0.png](http://upload-images.jianshu.io/upload_images/1921775-40c0d60acf555da7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

*這里需要注意: 同步函數和主隊列相結合時:  因為同步函數不會開新的線程,任務是在當前的線程中執行,如果當前線程是主線程,那么會造成死鎖,也就是隊列里面的任務都執行不了.造成的原因是: 因為主隊列中的任務是需要在主線程中執行,而在執行之前會先檢查主線程的狀態,如果發現主線程正在執行任務,那么就會暫停隊列中任務的調度,這里需要明白使用同步函數封裝任務時,任務的執行必須得拿到當前函數的返回值,才會繼續往下面執行, 因為每一條代碼都是一個任務,當主線程執行到當前同步函數時,也需要去執行里面封裝的block任務塊,而此時任務塊正在等主線程執行完任務,所以就沒辦法被執行,所以當前的同步函數就執行不完,也就拿不到返回值,所以就會執行不下去,造成了死鎖.
相反回到最開始,如果當前線程不是主線程,而是新開的子線程的話就不會造成死鎖,原因還是:主隊列本身的任務調度時的特點
所以切記: 主線程不能與主隊列結合使用.*

*異步函數與主隊列結合時不會造成死鎖,原因是: 首先明確當前情況下不會開新的線程,都是在主線程中執行的, 同樣主隊列中的任務執行前也會判斷主線程的狀態,當前主線程正在執行異步函數這個任務,所以也會暫停調度主隊列中的任務, 但是由于異步函數本身的特點,可以繼續往下執行,先把每個異步函數代碼都執行完之后,回頭再去執行每個異步函數里面封裝的任務.*

####GCD中其它常用的函數
- 主線程中延遲執行:dispatch_after
這個是最常用的,用來延遲執行的GCD方法,因為在主線程中我們不能用sleep來延遲方法的調用,所以用它是最合適的.

用三種隊列進行了測試
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"主隊列----%@",[NSThread currentThread]);
});

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
    NSLog(@"全局并發隊列----%@",[NSThread currentThread]);
});

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), serialDispatchQueue, ^{
    NSLog(@"串行隊列----%@",[NSThread currentThread]);
});
控制臺輸出結果

2017-05-24 18:07:22.709788 09-掌握-GCD的常用函數[14307:6637921] 主隊列----<NSThread: 0x17007a6c0>{number = 1, name = main}
2017-05-24 18:07:22.710247 09-掌握-GCD的常用函數[14307:6638005] 全局并發隊列----<NSThread: 0x170267bc0>{number = 3, name = (null)}
2017-05-24 18:07:22.710652 09-掌握-GCD的常用函數[14307:6638071] 串行隊列----<NSThread: 0x17026ba40>{number = 4, name = (null)}


結論:

a.GCD延遲執行的原理是,延遲把block內容提交到隊列而非直接添加到隊列后延遲調度
b.GCD可以通過設置隊列來控制block在哪個線程中調用
c.dispatch_after方法本身是異步的(類似dispatch_async) 具備開啟線程的能力


- 柵欄函數:dispatch_barrier_async
直接上代碼

//創建隊列
dispatch_queue_t queue = dispatch_queue_create("szl", DISPATCH_QUEUE_CONCURRENT);

//2添加任務
dispatch_async(queue, ^{
    for (NSInteger i = 0; i < 50; i++) {
        NSLog(@"1-%zd---%@",i,[NSThread currentThread]);
    }
    
});

dispatch_async(queue, ^{
    for (NSInteger i = 0; i < 10; i++) {
        NSLog(@"2-%zd---%@",i,[NSThread currentThread]);
    }
});

//控制順序:必須等任務1和2執行完畢之后才能執行任務3和任務4
dispatch_barrier_async(queue, ^{
    
    NSLog(@"+++++++++++++++++++++++++++");
});

dispatch_async(queue, ^{
    for (NSInteger i = 0; i <10; i++) {
        NSLog(@"3-%zd---%@",i,[NSThread currentThread]);
    }
});

dispatch_async(queue, ^{
    for (NSInteger i = 0; i <10; i++) {
        NSLog(@"4-%zd---%@",i,[NSThread currentThread]);
    }
});

控制臺輸出結果

2017-05-25 12:03:40.173683 多線程[14462:6789088] 1-0---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173840 多線程[14462:6789088] 1-1---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173877 多線程[14462:6789088] 1-2---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173910 多線程[14462:6789088] 1-3---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173943 多線程[14462:6789088] 1-4---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173976 多線程[14462:6789088] 1-5---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174008 多線程[14462:6789088] 1-6---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174041 多線程[14462:6789088] 1-7---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174073 多線程[14462:6789088] 1-8---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174106 多線程[14462:6789088] 1-9---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174169 多線程[14462:6789089] 2-0---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.176032 多線程[14462:6789088] 1-10---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177201 多線程[14462:6789089] 2-1---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177252 多線程[14462:6789088] 1-11---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177348 多線程[14462:6789088] 1-12---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177386 多線程[14462:6789088] 1-13---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177420 多線程[14462:6789088] 1-14---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177605 多線程[14462:6789088] 1-15---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177650 多線程[14462:6789088] 1-16---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177664 多線程[14462:6789089] 2-2---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177807 多線程[14462:6789088] 1-17---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177855 多線程[14462:6789089] 2-3---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177897 多線程[14462:6789088] 1-18---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177939 多線程[14462:6789089] 2-4---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177982 多線程[14462:6789088] 1-19---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.178287 多線程[14462:6789089] 2-5---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.178620 多線程[14462:6789088] 1-20---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.178678 多線程[14462:6789089] 2-6---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.178721 多線程[14462:6789088] 1-21---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.179618 多線程[14462:6789089] 2-7---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.180078 多線程[14462:6789088] 1-22---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180354 多線程[14462:6789089] 2-8---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.180430 多線程[14462:6789088] 1-23---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180476 多線程[14462:6789089] 2-9---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.180518 多線程[14462:6789088] 1-24---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180586 多線程[14462:6789088] 1-25---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180619 多線程[14462:6789088] 1-26---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180652 多線程[14462:6789088] 1-27---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182044 多線程[14462:6789088] 1-28---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182085 多線程[14462:6789088] 1-29---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182119 多線程[14462:6789088] 1-30---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182153 多線程[14462:6789088] 1-31---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182185 多線程[14462:6789088] 1-32---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182218 多線程[14462:6789088] 1-33---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182251 多線程[14462:6789088] 1-34---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182284 多線程[14462:6789088] 1-35---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182317 多線程[14462:6789088] 1-36---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182440 多線程[14462:6789088] 1-37---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182474 多線程[14462:6789088] 1-38---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182506 多線程[14462:6789088] 1-39---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182539 多線程[14462:6789088] 1-40---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182577 多線程[14462:6789088] 1-41---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182618 多線程[14462:6789088] 1-42---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182651 多線程[14462:6789088] 1-43---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182683 多線程[14462:6789088] 1-44---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182715 多線程[14462:6789088] 1-45---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190503 多線程[14462:6789088] 1-46---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190563 多線程[14462:6789088] 1-47---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190598 多線程[14462:6789088] 1-48---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190806 多線程[14462:6789088] 1-49---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190895 多線程[14462:6789090] +++++++++++++++++++++++++++
2017-05-25 12:03:40.191051 多線程[14462:6789090] 3-0---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191551 多線程[14462:6789090] 3-1---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191607 多線程[14462:6789090] 3-2---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191640 多線程[14462:6789090] 3-3---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191672 多線程[14462:6789090] 3-4---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191715 多線程[14462:6789088] 4-0---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192233 多線程[14462:6789090] 3-5---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192313 多線程[14462:6789090] 3-6---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192347 多線程[14462:6789090] 3-7---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192379 多線程[14462:6789090] 3-8---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192418 多線程[14462:6789090] 3-9---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192491 多線程[14462:6789088] 4-1---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192525 多線程[14462:6789088] 4-2---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192557 多線程[14462:6789088] 4-3---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192704 多線程[14462:6789088] 4-4---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192752 多線程[14462:6789088] 4-5---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192920 多線程[14462:6789088] 4-6---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.193071 多線程[14462:6789088] 4-7---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.193108 多線程[14462:6789088] 4-8---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.193698 多線程[14462:6789088] 4-9---<NSThread: 0x17407a6c0>{number = 3, name = (null)}

通過打印信息可以看到任務3和任務4是在任務1和任務2完全執行完之后才執行的

我們經常使用圖片下載框架SDWebImage里面就用到了這個柵欄函數來控制任務執行的順序 具體代碼可以在SDWebImage中搜索dispatch_barrier_async

- dispatch_group 
當我們需要監聽一個并發隊列中,所有任務都完成了,就可以用到這個group,因為并發隊列你并不知道哪一個是最后執行的,所以以單獨一個任務是無法監聽到這個點的,如果把這些單任務都放到同一個group,那么,我們就能通過dispatch_group_notify方法知道什么時候這些任務全部執行完成了。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{NSLog(@"0");});
dispatch_group_async(group, queue, ^{NSLog(@"1");});
dispatch_group_async(group, queue, ^{NSLog(@"2");});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"down");});

在例子中,我把3個log分別放在并發隊列中,通過把這個并發隊列任務統一加入group中,group每次runloop的時候都會調用一個方法dispatch_group_wait(group, DISPATCH_TIME_NOW),用來檢查group中的任務是否已經完成,如果已經完成了,那么會執行dispatch_group_notify的block,輸出’down’看一下運行結果:

2017-05-25 14:32:48.965429 多線程[14500:6812849] 0
2017-05-25 14:32:48.965513 多線程[14500:6812849] 1
2017-05-25 14:32:48.965533 多線程[14500:6812849] 2
2017-05-25 14:32:49.067608 多線程[14500:6812807] down

- dispatch_apply 快速迭代
這個方法用于無序查找,在一個數組中,我們能開啟多個線程來查找所需要的值
NSArray *array=[[NSArray      alloc ]  initWithObjects:@"0",@"1",@"2",@"3",@"4",@"5",@"6", nil];

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);//創建隊列
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%zd---%@----%@",index, [array objectAtIndex:index], [NSThread currentThread]);
});
輸出結果

2017-05-25 14:40:23.350677 多線程[14505:6815659] 0---0---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.350668 多線程[14505:6815678] 1---1---<NSThread: 0x1700700c0>{number = 3, name = (null)}
2017-05-25 14:40:23.350821 多線程[14505:6815659] 2---2---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.350962 多線程[14505:6815659] 4---4---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.351006 多線程[14505:6815659] 5---5---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.351049 多線程[14505:6815659] 6---6---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.351084 多線程[14505:6815678] 3---3---<NSThread: 0x1700700c0>{number = 3, name = (null)}

可以看到參與迭代遍歷的有子線程也有主線程,這里就要注意一個點:主線程也會參與迭代的過程,里面的任務是并發執行的,所以迭代時不能傳主隊列.

- dispatch_once
   這個經常使用,創建單例的時候
   整個應用程序中只會執行一次
   本身是線程安全的,所以不用去擔心線程安全的問題
        onceToken 是用來記錄該部分代碼是否被執行過  
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ //第一次執行代碼時 onecToken值為0,之后就都是 -1 
                    
    NSLog(@"--once---%@",[NSThread currentThread]);
});

#####線程安全與線程間通訊

- 線程安全
多線程安全隱患:
資源共享,多個線程同時訪問同一塊資源,比如同時訪問同一個對象,同一個變量,同一個文件, 這個時候就容易引發多線程安全的問題,容易引發數據錯亂和數據安全的問題.

解決方案:  加鎖

加互斥鎖
把相關代碼 加在  @synchronized(objc){} 中,()中的objc稱為鎖對象,只要是唯一的就可以.當每個線程來執行加鎖里面的代碼時會先加把鎖,執行完之后再解鎖.這樣下條線程會等待之前線程執行完之后,再去執行.

舉例

@synchronized(self) {
//1.檢查余票
NSInteger count = self.totalCount;
if (count >0) {
for (NSInteger i = 0; i<100000; i++) {
//演示耗時操作
}
//2.賣出去一張票
self.totalCount = count - 1;
NSLog(@"%@賣出去了一張票,還剩下%zd張票",[NSThread currentThread].name,self.totalCount);
}else
{
NSLog(@"票已經賣光了,做灰機回去吧~");
break;
}
}

注意點:
         1)鎖定一份代碼只能使用同一把鎖,
         2)加鎖的時候要注意位置
         3)加鎖有前提條件:只有當多個線程存在搶奪同一塊資源的時候才需要加鎖
         4)加鎖是需要耗費性能的
         5)造成線程同步(多個線程按照固定的順序來執行任務)

加同步鎖 (NSLock)
舉例
    NSLock *lock = [[NSLock alloc] init];
    [lock lock];
    
    //1.檢查余票
    NSInteger count = self.totalCount;
    if (count >0) {
        for (NSInteger i = 0; i<100000; i++) {
            //演示耗時操作
        }
        //2.賣出去一張票
        self.totalCount = count - 1;
        NSLog(@"%@賣出去了一張票,還剩下%zd張票",[NSThread currentThread].name,self.totalCount);
    }else
    {
        NSLog(@"票已經賣光了,做灰機回去吧~");
        break;
    }
   
    [lock unlock];
AFNetWorking中用NSLock比較多一點 可以在AFNetWorking中搜索unlock方法進行查看具體使用

其它的加鎖方式還有:
NSRecursiveLock :遞歸鎖,有時候“加鎖代碼”中存在遞歸調用,遞歸開始前加鎖,遞歸調用開始后會重復執行此方法以至于反復執行加鎖代碼最終造成死鎖,這個時候可以使用遞歸鎖來解決。使用遞歸鎖可以在一個線程中反復獲取鎖而不造成死鎖,這個過程中會記錄獲取鎖和釋放鎖的次數,只有最后兩者平衡鎖才被最終釋放。

NSDistributedLock:分布鎖,它本身是一個互斥鎖,基于文件方式實現鎖機制,可以跨進程訪問。

pthread_mutex_t:同步鎖,基于C語言的同步鎖機制,使用方法與其他同步鎖機制類似。

- 原子性和非原子性
我們平時在開發中定義屬性時有以下兩種選擇
atomic:原子性,線程相對來說是安全的,因為它會為該屬性的setter方法加鎖,但是性能會影響
nonatomic:非原子性,線程是不安全的,不會為setter方法加鎖.性能會好
關于這兩個關鍵字的具體介紹參考下面的文章:
https://www.douban.com/note/486901956/

- 線程間通訊
舉個下載圖片的例子
GCD:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSURL *url = [NSURL URLWithString:@"http://dimg07.c- ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];
    
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    UIImage *image = [UIImage imageWithData:data];
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        self.imageView.image = image;
    });
    
});

NSThread:

//開線程
[NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];

download

// 具體下載
NSURL *url = [NSURL URLWithString:@"http://dimg07.c-ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];

NSData *imageData = [NSData dataWithContentsOfURL:url];

UIImage *image = [UIImage imageWithData:imageData];

NSLog(@"dowbnload=====%@",[NSThread currentThread]);

//4.回到主線程設置圖片
/*
 第一個參數:回到主線程要調用的方法
 第二個參數:回到主線程需要傳遞的參數 image
 第三個參數:要不要等待調用方法結束之后再繼續往下走
 */
//[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
//NSLog(@"---end----");

[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];

//    [self.imageView  performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容