1. 程式人生 > >iOS-GCD多執行緒

iOS-GCD多執行緒

1、GCD基本知識

學習 GCD 之前,先來了解 GCD 中兩個核心概念:任務佇列

任務:當前執行的操作,換句話說就是現線上程中執行的那段程式碼,在 GCD 中是放在 block 中的。

任務的執行有兩種方式:同步執行(sync)非同步執行(async)。兩者的主要區別是:是否等待佇列的任務執行結束,以及是否具備開啟新執行緒的能力。

  • 同步執行(sync)
    • 同步新增任務到指定的佇列中,需要等待這個佇列排在它之前的任務執行完了之後才會輪到它執行,舉個栗子:你現在在吃飯,這時睡覺的任務新增進來,你只有吃完飯後才能去睡覺。
    • 只能在當前執行緒中執行任務,不具備開啟新執行緒的能力。
  • 非同步執行(async)
    • 非同步新增任務到指定的佇列中,它不會做任何等待,當前佇列任務可以繼續執行。舉個栗子:你現在在吃飯,這時有一個電話打過來,你就可以邊吃飯邊打電話。
    • 可以在新的執行緒中執行任務,具備開啟新執行緒的能力。

注意:非同步執行(async)雖然具有開啟新執行緒的能力,但是並不一定會開啟新執行緒。這跟任務所指定的佇列型別有關。

佇列(Dispatch Queue):這裡的佇列指執行任務的等待佇列,即用來存放任務的佇列。佇列是一種特殊的線性表,採用 FIFO(先進先出)的原則,即新任務總是被插入到佇列的末尾,而讀取任務的時候總是從佇列的頭部開始讀取。每讀取一個任務,則從佇列中釋放一個任務。

在 GCD 中有兩種佇列:序列佇列併發佇列。兩者都符合 FIFO(先進先出)的原則。兩者的主要區別是:執行順序不同,以及開啟執行緒數不同。

  • 序列佇列(Serial Dispatch Queue)
    • 每次只有一個任務被執行。讓任務一個接著一個地執行。(只開啟一個執行緒,一個任務執行完畢後,再執行下一個任務)
  • 併發佇列(Concurrent Dispatch Queue)
    • 可以讓多個任務併發(同時)執行。(可以開啟多個執行緒,並且同時執行任務)

注意:併發佇列的併發功能只有在非同步(dispatch_async)函式下才有效

2、GCD基本使用

GCD 的使用步驟其實很簡單,只有兩步。

  1. 建立一個佇列(序列佇列或併發佇列)
  2. 將任務追加到任務的等待佇列中,然後系統就會根據任務型別執行任務(同步執行或非同步執行)
    //建立序列佇列
    dispatch_queue_t serialQueue = dispatch_queue_create("佇列名字", DISPATCH_QUEUE_SERIAL);
    //建立併發佇列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("佇列名字", DISPATCH_QUEUE_CONCURRENT);

 不過一般情況下我們不用自己建立佇列,而是使用系統提供的佇列。

對於序列佇列,GCD 提供了的一種特殊的序列佇列:主佇列(Main Dispatch Queue)

  • 所有放在主佇列中的任務,都會放到主執行緒中執行。
  • 可使用dispatch_get_main_queue()獲得主佇列。

對於併發佇列,GCD 預設提供了全域性併發佇列(Global Dispatch Queue),其實全域性併發佇列可以當做普通併發佇列使用。

  • 可以使用dispatch_get_global_queue來獲取。需要傳入兩個引數。第一個引數表示佇列優先順序,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個引數暫時沒用,用0即可。

根據執行方式和佇列型別,我們就有六種組合方式。

區別 併發佇列 序列佇列 主佇列
同步(sync) 沒有開啟新執行緒,序列執行任務 沒有開啟新執行緒,序列執行任務 主執行緒呼叫:死鎖卡住不執行(注意) 其他執行緒呼叫:沒有開啟新執行緒,序列執行任務
非同步(async) 有開啟新執行緒,併發執行任務(常用) 有開啟新執行緒(1條),序列執行任務 沒有開啟新執行緒,序列執行任務(常用)

3、GCD 執行緒間的通訊

在iOS開發過程中,我們一般在主執行緒裡邊進行UI介面重新整理,例如:改變控制元件內容、點選、滾動、拖拽等事件。

我們通常把一些耗時的操作放在其他執行緒,比如說圖片下載、檔案上傳等耗時操作。

而當我們有時候在其他執行緒完成了耗時操作時,需要回到主執行緒,那麼就用到了執行緒之間的通訊。

    //這是常用用法
    dispatch_async(ISGetGlobalQueue, ^{
        //非同步執行併發佇列,執行耗時操作
        //執行完畢後,非同步執行主佇列
       dispatch_async(ISGetMainQueue, ^{
           //執行更新UI介面等操作
       });
    });