iOS多線程編程之GCD詳解(一)

1. GCD簡介

iOS開發中多線程的API主要有pthread,NSThread,NSOperation和GCD,前兩者在現在開發過程中已經不常用,NSOperation是面向對象封裝的一套API,而GCD則是一套純C語言API。

引用下百度的介紹
GCD為Grand Central Dispatch的縮寫。
Grand Central Dispatch (GCD)是Apple開發的一個多核編程的較新的解決方法。它主要用于優化應用程序以支持多核處理器以及其他對稱多處理系統。它是一個在線程池模式的基礎上執行的并行任務。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。

GCD有哪些優點:

  1. 提供了易于使用的并發模型而不僅僅只是鎖和線程,可以讓我們輕松的進行多線程編程。
  2. 自動利用更多的CPU內核。
  3. 自動管理線程的生命周期,以及多線程調度。
  4. 提供了簡單創建單例的接口等等。

2. Dispatch_Queue(隊列)概念

隊列:

GCD中的隊列和數據結構中的隊列特性上一致,都是受限制的線性表,遵循FIFO(First In First Out),即新的任務需要在隊尾加入,讀取任務則從隊首部讀取,即先進先出
隊列又分為:

  • 串行隊列(Serial Dispatch Queue)
    簡單理解串行隊列就是按順序執行一個任務,上一個任務執行完畢后執行下一個任務,現實中的例子,去銀行辦業務,只有一個窗口,大家有秩序的排隊辦業務,即是串行隊列

  • 并發隊列(Concurrent Dispatch Queue)
    簡單理解并發隊列就是同時執行多個任務,現實中的極端例子,去銀行排隊辦業務,有多個窗口同時可以辦業務,即是并發隊列,需要注意的是,這些任務會按照被添加的順序依次開始執行。但是任務完成的順序是任意的。

這里提到了“任務”的概念:
“任務”,在 GCD 里指的是 Block,即一段需要執行的代碼塊
任務執行方式有兩種:

  • 同步執行(dispatch_sync)
    完成任務后才會返回,進行下一任務,可見同步不具備開啟線程能力,只會在當前線程依次執行

  • 異步執行(dispatch_async)
    完成任務后立即返回,進行下一任務,具備多線程能力

總結:并發隊列只會在異步執行下生效,同步執行不會觸發多線程創建。

3. GCD隊列編程實現

  1. 創建一個隊列
//串行隊列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    
    //并發隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_queue_create創建一個dispatch_queue_t隊列,第一個參數設置該隊列標識符,用于調試使用,第二個參數,則是隊列類型
DISPATCH_QUEUE_SERIAL串行隊列
DISPATCH_QUEUE_CONCURRENT 并發隊列

除了自己創建隊列之外,系統提供了兩個隊列供我們獲取使用

    //主線程隊列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //全局隊列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
  • 主線程隊列:這個是主線程的串行隊列
  • 全局隊列: 這是個全局的并發隊列,很多時候可以不需要自己創建并發隊列,直接獲取全局隊列即可 第一個參數為優先級,這是個大概優先級設置
DISPATCH_QUEUE_PRIORITY_HIGH  //高優先級
DISPATCH_QUEUE_PRIORITY_DEFAULT //默認優先級
DISPATCH_QUEUE_PRIORITY_LOW //低優先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND //后臺優先級 

接下來即可在隊列中添加任務執行

//異步執行
    dispatch_async(serialQueue, ^{
        NSLog(@"serialQueue中異步執行");
    });
    
    //同步執行
    dispatch_sync(serialQueue, ^{
        NSLog(@"serialQueue中同步執行");
    });

這里總共有三種類型隊列:

  1. 并發隊列(包括全局隊列)
  2. 串行隊列
  3. 主線程隊列

三種隊列分別可以同步異步執行就有6種組合方式

  • 主線程同步執行
  • 主線程異步執行
  • 串行隊列同步執行
  • 串行隊列異步執行
  • 并行隊列同步執行
  • 并行隊列異步執行

接下來我們依次看下每種情況

3.1 主線程同步執行

    NSLog(@"Begin");
    
    //主線程同步同步執行
    dispatch_sync(mainQueue, ^{
        NSLog(@"主線程同步同步執行");
    });
    
    NSLog(@"End");

運行下我們會發現

2017-05-03 16:50:31.542 GCDDemo[41489:596607] Begin
只打印了begin,并沒有執行下去
事實上這里發生了死鎖,
之前說過dispatch_sync會等待上一個任務執行完才會執行下一個任務,sync添加的任務需要執行,需要等待NSLog(@"End");執行完畢,```NSLog(@"End");``任務本身也添加在主線程隊列中,所以執行這個任務的前提是sync添加的任務執行完畢,這就出現了死鎖,兩個任務互相等待

總結:
1. 主線程中執行同步任務會發生死鎖
2. 串行隊列中嵌套串行隊列任務會發生死鎖(這個留給大家自己驗證,本質上和主線程同步發生死鎖一致)

3.2 主線程異步執行

    NSLog(@"Begin");
    //主線程異步執行
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------主線程異步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------主線程異步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------主線程異步執行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

運行結果

2017-05-03 17:09:26.031 GCDDemo[42671:613068] Begin
2017-05-03 17:09:26.031 GCDDemo[42671:613068] End
2017-05-03 17:09:26.042 GCDDemo[42671:613068] 0---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}

2017-05-03 17:09:26.043 GCDDemo[42671:613068] 1---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.044 GCDDemo[42671:613068] 2---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.052 GCDDemo[42671:613068] 0---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.057 GCDDemo[42671:613068] 1---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.059 GCDDemo[42671:613068] 2---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.060 GCDDemo[42671:613068] 0---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.062 GCDDemo[42671:613068] 1---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.063 GCDDemo[42671:613068] 2---------主線程異步執行<NSThread: 0x60000007d5c0>{number = 1, name = main}

總結:
主線程隊列異步執行不會開辟線程,會在當前線程同步執行。

3.3 串行隊列同步執行

   NSLog(@"Begin");
    //串行隊列同步執行
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------串行隊列同步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行隊列同步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行隊列同步執行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

運行結果如下:

2017-05-03 17:25:48.225 GCDDemo[43694:625941] Begin
2017-05-03 17:25:48.225 GCDDemo[43694:625941] 0---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 1---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 2---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 0---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 1---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 2---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 0---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 1---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 2---------串行隊列同步執行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] End

總結: 串行隊列異步執行不會開辟多線程,只會在一條線程中依次執行

3.4 串行隊列異步執行

    NSLog(@"Begin");
    //串行隊列異步執行
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------串行隊列異步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行隊列異步執行%@", i,[NSThread currentThread]);
        }
    });
 dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------串行隊列異步執行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

運行結果如下

2017-05-03 17:20:16.514 GCDDemo[43349:621375] Begin
2017-05-03 17:20:16.514 GCDDemo[43349:621375] End
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 0---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 1---------串行隊列同步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 2---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 1---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 2---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 1---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 2---------串行隊列異步執行<NSThread: 0x608000268040>{number = 3, name = (null)}

總結: 串行隊列異步執行不會開辟多線程,只會在一條線程中依次執行

細心的同學會發現串行隊列同步和異步執行都沒有開辟多線程,在一條線程中同步執行,那么對于串行隊列同步和異步執行有什么區別呢?
區別只有一點:

dispatch_async:不會阻塞當前隊列,立即返回添加當前隊列后面任務,可以看到上圖打印結果,先打印end。再打印async任務

dispatch_sync:會阻塞當前隊列,等該sync任務全部執行完畢之后再添加當前隊列后面任務,可以看到上圖打印結果,先打印完sync任務打印end。

3.5 并發隊列同步執行

    NSLog(@"Begin");
    //串行隊列同步執行
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------并發隊列同步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并發隊列同步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并發隊列同步執行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

運行結果如下:

2017-05-03 17:34:24.407 GCDDemo[44222:633000] Begin
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 0---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 1---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.409 GCDDemo[44222:633000] 2---------并發隊列同步執行<NSThread: 0x60800006f380>{number = 1, name = main}
2017-05-03 17:34:24.410 GCDDemo[44222:633000] End

總結: 并發隊列同步執行不會開辟多線程,只會在一條線程中依次執行

3.6 并發隊列異步執行

    NSLog(@"Begin");
    //并發隊列異步執行
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
           NSLog(@"%d---------并發隊列異步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并發隊列異步執行%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i ++) {
            NSLog(@"%d---------并發隊列異步執行%@", i,[NSThread currentThread]);
        }
    });
    NSLog(@"End");

運行結果如下

2017-05-03 17:38:10.893 GCDDemo[44461:636216] Begin
2017-05-03 17:38:10.894 GCDDemo[44461:636216] End
2017-05-03 17:38:10.894 GCDDemo[44461:636252] 0---------并發隊列異步執行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636254] 0---------并發隊列異步執行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636251] 0---------并發隊列異步執行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.897 GCDDemo[44461:636252] 1---------并發隊列異步執行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.899 GCDDemo[44461:636254] 1---------并發隊列異步執行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.900 GCDDemo[44461:636251] 1---------并發隊列異步執行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.903 GCDDemo[44461:636252] 2---------并發隊列異步執行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.904 GCDDemo[44461:636254] 2---------并發隊列異步執行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.905 GCDDemo[44461:636251] 2---------并發隊列異步執行<NSThread: 0x60800007a800>{number = 3, name = (null)}

總結: 并發隊列異步執行會開辟多線程執行,并且執行順序不定

各種組合的結果總結如下表

同步執行 異步執行
主線程隊列 出現死鎖 不會開辟多線程,在主線程上串行執行
串行隊列 不會開辟多線程,在一條線程上串行執行 不會開辟多線程,在一條線程上串行執行
并發隊列(全局隊列) 不會開辟多線程,在一條線程上串行執行 開辟多線程執行,并且執行順序不定

劃重點:
1. 并發隊列只會在異步執行下才會開啟多線程執行
2. 在主線程隊列同步執行,或者串行隊列嵌套串行隊列同步任務會發生死鎖
3. dispatch_async: 不會阻塞當前隊列,立即返回添加當前線程后面任務。
4. dispatch_sync:會阻塞當前隊列,等該sync任務全部執行完畢之后再添加當前隊列后面任務

4.應用示例

  1. 異步處理數據完成后,主線程更新UI界面
dispatch_async(globalQueue, ^{
        //異步數據處理...
        dispatch_async(mainQueue, ^{
            //主線程更新UI
            
        });
    });

可以看到運用GCD可以輕松的進行線程間通信

  1. 使用指定隊列進行串行處理任務,例如數據庫存儲等依賴線程安全等處理
//串行隊列保證線程安全
    dispatch_sync(serialQueue, ^{
        //數據存儲等依賴線程安全操作...
    });

FMDBDataBaseQueue就是使用的串行隊列來保證線程安全的

5. 其他GCD API

5.1 dispatch_after

dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"After");
    });

延遲執行,有時候可以作為定時執行作用,需要注意的是,該函數并不是在指定時間后執行處理,而只是在指定時間追加處理到dispatch_queue,實際執行時間受到runloop的狀態影響,存在偏差。

5.2 dispatch_once

static dispatch_once_t onceToken;  
    dispatch_once(&onceToken, ^{  
        sharedManager = [[SchoolManager alloc] init];  
    });  

執行一次,在block中的代碼全局只會執行一次,被廣泛用于單例創建中

5.3 dispatch_suspend / dispatch_resume

//掛起隊列 
dispatch_suspend(queque);
//恢復隊列
 dispatch_resume(queue);

掛起對已經執行的任務沒有影響,會暫停所有未執行的任務以及后續追加的任務
恢復則會繼續執行所有被掛起的任務

5.4 dispatch_set_target_queue

//搬運一段代碼

dispatch_queue_t mySerialDispatchQueue =
    dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground =
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);

dispatch_set_target_queue主要有兩個作用

  1. 設置優先級,自建的隊列優先級默認和系統隊列優先級一致,設置參數1隊列的優先級和參數2的優先級一致,顯然你不能設置系統全局隊列和主隊列優先級
  2. 更改隊列的執行層級,如果多個串行隊列設置函數目標串行隊列是某一個串行隊列,原本并發執行的串行隊列,在目標串行隊列上只能依次執行,代碼示例如下
    dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    dispatch_set_target_queue(queue3, targetQueue);
    dispatch_async(queue1, ^{
        NSLog(@"1 in");
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"1 out");
    });
    dispatch_async(queue2, ^{
        NSLog(@"2 in");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"2 out");
    });
    dispatch_async(queue3, ^{
        NSLog(@"3 in");
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"3 out");
    });

5.5 dispatch_group

主要應對這樣的需求,異步處理完A和B任務,兩者都執行完執行C任務,和NSOperation中的依賴一致。示例如下

 dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //異步耗時操作A
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //異步耗時操作B
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 前面的異步操作A和B都執行完之后,回主線程
    });

5.6 dispatch_apply

這是dispatch_syncdispatch_group的關聯API,按指定次數將指定的Block追加到指定的Dispatch_Queue中,并且等待全部執行結束。可以用于遍歷效果

//全局隊列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, globalQueue, ^(size_t index) {
        //重復執行10次
        NSLog(@"%zu",index);
    });
    
    //10次執行完之后,再執行Done
    NSLog(@"Done");

5.7 dispatch_barrier_sync / dispatch_barrier_async

柵欄:有時候創建兩組并發任務,如果在中間加入柵欄,那么這個任務會在第一組任務完成后執行,并且第二組任務會在柵欄任務完成后才開始執行,如下圖所示在并發隊列中添加任務,執行順序一定是
任務組A->Barrier任務->任務組B

dispatch_barrier.png

示例代碼如下:

dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^{
        NSLog(@"1---------");
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"2--------");
    });

    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"barrier--------");
    });

    dispatch_async(concurrentQueue, ^{
        NSLog(@"3--------");
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"4--------");
    });

打印執行順序1,2不定,3,4也不定,但是barrier一定在1和2之后,3和4一定在barrier之后,可以自行添加數量測試。

應用場景,經常我們會自行創建一個隊列進行文件讀取和存儲,一般文件讀取的速度很快,可以使用并發隊列多線程提高讀取效率,但是文件存儲需要考慮到線程安全,那么我們就可以使用barrier進行文件存儲操作,類似這樣

dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^{
        //文件讀取
    });
    dispatch_async(concurrentQueue, ^{
        //文件讀取
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        //文件存儲
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        //文件存儲
    });
    dispatch_async(concurrentQueue, ^{
        //文件讀取
    });

可見使用barrier可以輕松高效的實現文件IO。

dispatch_barrier需要注意的點

  1. dispatch_barrier只會對自建的隊列生效,對于系統的mainQueue和GlobalQueue不起作用
  2. dispatch_barrier_asyncdispatch_barrier_sync的區別也同樣在于同步和異步,dispatch_barrier_async不會等待自己任務執行完畢才會在隊列中添加其他任務,而dispatch_barrier_sync會等待自己任務執行完畢后才會在隊列中添加其他任務。

AFNetworking中大量使用dispatch_barrier_async做數據存儲,可以看到dispatch_barrier_async也可以實現串行同步隊列效果,相比于dispatch_sync容易產生死鎖(在串行隊列中同步添加該串行隊列任務即會發生死鎖),dispatch_barrier_async更加安全。

全文總結:
以上介紹了GCD中絕大多數常用API,可以看到GCD的靈活性,通過在應用中合理使用GCD來提高程序的執行效率。

參考書籍:
Objective-C高級編程

后篇:
iOS多線程編程之GCD詳解(二)完結

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

推薦閱讀更多精彩內容

  • 一、前言 上一篇文章iOS多線程淺匯-原理篇中整理了一些有關多線程的基本概念。本篇博文介紹的是iOS中常用的幾個多...
    nuclear閱讀 2,072評論 6 18
  • 一、前言 本篇博文介紹的是iOS中常用的幾個多線程技術: NSThread GCD NSOperation 由于a...
    和玨貓閱讀 588評論 0 1
  • 1. GCD簡介 什么是GCD呢?我們先來看看百度百科的解釋簡單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 401評論 0 0
  • 在這篇文章中,我將為你整理一下 iOS 開發中幾種多線程方案,以及其使用方法和注意事項。當然也會給出幾種多線程的案...
    張戰威ican閱讀 615評論 0 0
  • “死亡需要勇氣,但痛苦地活著才是真正的勇士。” 一開始注意到《萬箭穿心》這部作品,是因為演員顏丙燕號稱可...
    還牽著你的手閱讀 847評論 0 0