多線程之GCD


Runloop

  • 什么是Runloop

    1. Runloop就是消息循環,每一個線程內部都有一個消息循壞。
    2. 只有主線程的消息循環默認開啟,子線程的消息循環默認不開啟。
  • 消息循環的目的

    1. 保證程序不退出。
    2. 負責處理輸入事件。Runloop接收輸入事件來自兩種不同的來源:輸入源(input source)定時源(timer source)
    3. 如果沒有事件發生,會讓程序進入休眠狀態。
  • 使用消息循環的時候必須指定兩件事情

    1. 輸入事件:輸入源和定時源。
    2. 消息循環模式,此模式必須跟當前消息循環使用的模式匹配。
  • 消息循環模式

    1. NSDefaultRunLoopMode
    2. NDRunLoopCommonModes
  • 子線程的Runloop

    • 啟動子線程的消息循環
啟動子線程的消息循環

GCD

  • 什么是GCD

    1. 全稱是Grand Center Dispatch(CPU調度中心,調度的是任務和線程,把任務交給線程執行)。
    2. 純C語言,提供了非常多、強大的函數。
    3. GCD是 libdispatch的統稱,而libdispatch是蘋果為了在多核硬件(如iOS、OSX)上提供處理并發代碼的庫。
  • GCD的優勢

    1. GCD是蘋果公司為多核的并行運算提出的解決方案。
    2. GCD會自動利用更多的CPU內核。
    3. GCD會自動管理線程的生命周期(創建線程、調度任務、銷毀線程)。
    4. 只要告訴GCD想要執行什么任務,不需要編寫任何線程管理的代碼。
    5. GCD能幫助你分配繁重計算任務并且保持在后臺運行,幫你提高App的響應程度。
    6. GCD幫你提供易用的并發模式,不僅僅是鎖和線程,幫助你避免在開發并發程序時的bug。
    7. GCD具有在常見模式(如單例模式)以最原始的語句優化,是你的代碼具有更高的效率。
  • GCD術語

    1. 串行并行:所謂串行并行描述是相對而言的,串行是指在同一時間只執行一個任務,并行是指在同一時間可能執行多個任務。
    2. 同步異步:在GCD中,同步異步是為了描述一個函數相對于該函數要求GCD執行完成的另一個任務。同步方法只在它完成它需要做的任務后才會返回。異步方法剛好和同步方法相反,它不會等待任務完成才返回,它會立即返回。所以異步不會阻塞當前線程執行另一個任務(方法\函數)。
    3. 死鎖:兩個(有時更多)東西——在大多數情況下,是線程——所謂的死鎖是指它們都卡住了,并等待對方完成或執行其它操作。第一個不能完成是因為它在等待第二個的完成。但第二個也不能完成,因為它在等待第一個的完成。
    4. 線程安全:線程安全的代碼在多線程或并發任務中被安全的調用,而不會導致任何問題(數據損壞、崩潰等)。線程不安全的代碼在某個時刻只能在一個上下文中運行。一個線程安全代碼的例子是NSDictionary,你可以在同一時間在多個線程中使用它而不會有問題,另一方面,NSMutableDictionary就不是線程安全的,應該保證一次只能一個線程訪問它。
  • GCD使用步驟

    1. 定制任務:確定要執行的操作(方法)。
    2. 將任務添加到隊列中,并按照指定的同步或異步方式執行任務。
      • GCD會自動將隊列中的任務取出,放到對應的線程中執行。
      • 任務的取出遵循隊列的先進先出原則。
  • 隊列

    1. GCD提供了dispatch queues(調度隊列)來執行代碼段,這些隊列以FIFO(先進先出)的方式來管理你用GCD提交的任務。這保證了你先提交的任務先執行,即第一個任務添加到隊列中就第一個開始執行,第二個添加的任務將第二個執行,直到隊列的最后一個任務。
    2. 所有的調度隊列(dispatch queues)自身都是線程安全的,你能通過多個現成來訪問它們。
    3. 串行隊列:在串行隊列中,同一個隊列中只有一個任務在執行,每個任務只有在前一個任務執行完成后才能開始執行。而且,你不知道在一個Block(任務)執行結束到下一個Block(任務)開始執行之間的這段時間是多長。這些任務的執行時機完全是在GCD的控制之下,你能唯一保證的是:GCD一次只能執行一個任務以及它會按照你添加到隊列中的先后順序來執行任務。在串行隊列中,不會有兩個任務并發執行。
    4. 并發隊列:在并發隊列中,你唯一能保證的是,這些任務會按照被添加的順序開始執行,但是任務可以以任何順序完成,你不知道在執行下一個任務是從什么時候開始,或者說任意時刻有多個Block(任務)執行,這個完全是取決于GCD。
    5. 在什么時候執行Block完全是由GCD來決定,如果一個Block的執行時間與另外一個Block重疊,GCD就會決定是否將另一個任務運行在不同的核上,除非那個核不能使用,否則GCD將通過切換上下文方式將任務運行在另一個核上。
  • GCD的核心概念

    1. GCD的核心概念
      • 任務:要執行的操作(方法)
      • 隊列:用來存放任務的集合
      • 將任務添加到隊列,指定任務執行的方法。
    2. 任務:使用block封裝,block就是一個提前準備好的代碼塊,在需要的時候執行。
    3. 隊列
      • 串行隊列:一個接一個的調度任務。
      • 并發隊列:可以同時調度多個任務。
      • 主隊列:全局串行隊列,由主線程串行調度任務,并且只有一個。
      • 全局隊列:沒有名稱的并發隊列。
    4. 執行任務的函數:
      • 同步執行:當前指令不完成,就不會執行下一條指令。
      • 異步執行:當前指令不完成,同樣可以執行下一條指令。

隊列和執行的幾種情況

  • 串行隊列,同步任務
    1. 不新開線程,順序(同步)執行。
    2. 在當前線程執行。
串行隊列,同步任務
  • 串行隊列,異步任務
    1. 開一個線程,順序(同步)執行。
    2. 只有一個線程,因為是串行隊列,只有一個線程就可以按順序執行隊列中的所有任務。
串行隊列,異步任務
  • 并發隊列,異步任務
    1. 開多個線程,異步執行。
    2. 每次開啟多少個線程是不固定的(線程數,不由我們控制)。
    3. 線程數由GCD來決定的。
并發隊列,異步任務
  • 并發隊列,同步任務
    • 不新開線程,順序(同步)執行。
    • 和串行隊列同步執行效果相同。
并發隊列,同步任務
  • 主隊列,異步任務
    1. 不新開線程,同步執行。
    2. 主隊列特點:如果主線程正在執行代碼暫時不調度任務,等主線程執行結束后再執行任務。
    3. 主隊列又叫全局串行隊列,主隊列的任務都由主線程來調度,不會開啟新線程。
主隊列,異步任務
  • 主隊列,同步任務
    1. 程序執行不出來,造成“死鎖”。
    2. 死鎖的原因,當程序執行到這段代碼的時候:
      • 主隊列:如果主線程正在執行代碼,就不調度任務。
      • 同步任務:如果第一個任務沒有執行,就繼續等待第一個任務執行完成,再執行下一個任務。
      • 此時互相等待,程序無法往下執行(死鎖)。
    3. 主隊列和串行隊列的區別:
      • 串行隊列:必須等待一個任務執行完成,再調度另一個任務。
      • 主隊列:以先進先出調度任務,如果主線程上有代碼在執行,主隊列不會調度任務。
主隊列,同步任務
  • 解決死鎖
    • 將(主隊列,同步任務)放入非主隊列異步任務中,可以解決死鎖。
    • (主隊列,同步任務)在子線程中運行,同步執行不用等待主線程執行此同步執行的任務。
解決死鎖
  • 總結
同步\異步 全局并行隊列 手動創建串行隊列 主隊列
同步(sync) 沒有開啟新線程 串行執行任務 沒有開啟新線程 串行執行任務 會死鎖
異步(async) 開啟新線程 并行執行任務 開啟新線程 并行執行任務 沒有開啟新線程 串行執行任務

同步任務

  • 同步任務的作用

    • 在網絡開發中,通常會把很多任務放在后臺異步執行,有些任務會彼此“依賴”,應該使用同步任務處理。
  • 同步任務的特點

    • 隊列調度多個異步任務前,指定一個或者多個同步任務,讓所有的異步任務都等待同步任務完成,這就是所謂的“依賴”關系。
演示后臺有依賴關系的耗時操作

全局隊列

  • 全局隊列
    1. 全局隊列的本質就是并發隊列
    2. 獲取全局隊列最簡單方式dispatch_get_global_queue(0,0);
// 獲取全局隊列的函數
dispatch_get_global_queue(long identifier, unsigned long flags);

// 第一個參數 identifier
// 在iOS7中表示調度的優先級(讓線程響應的更快還是更慢)
DISPATCH_QUEUE_PRIORITY_HIGH 2 高優先級
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認優先級
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺優先級
// 在iOS8中表示服務質量
QOS_CLASS_USER_INTERACTIVE 用戶希望線程快點執行完畢 不要使用耗時操作
QOS_CLASS_USER_INITIATED 用戶需要的 不要使用耗時操作
QOS_CLASS_DEFAULT 默認
QOS_CLASS_UTILITY 耗時操作
QOS_CLASS_BACKGROUND 后臺
QOS_CLASS_UNSPECIFIED 0 未指定優先級
// 為了在iOS7和iOS8中適配此參數 可以直接傳入0
DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND

// 第二個參數 為將來保留使用 始終傳入0
  • 全局隊列和并發隊列的區別
    1. 并發隊列有名稱,可以跟蹤錯誤。全局隊列沒有名稱。
    2. 在ARC中不需要考慮釋放內存,dispatch_release(q);不允許調用。
    3. 在MRC中需要手動管理內存,并發隊列是create創建出來的,在MRC中見到create就要release,全局隊列則不需要release,全局隊列只有一個。
    4. 一般我們使用全局隊列。

延時操作

  • 延時操作
// dispatch_after函數的定義
dispatch_after(dispatch_time_t when,
    dispatch_queue_t queue,
    dispatch_block_t block);
// dispatch_after的參數
// 參數1 dispatch_time_t when 多少納秒之后執行
// 參數2 dispatch_queue_t queue 任務添加到哪個隊列
// 參數3 dispatch_block_t block 要執行的任務
指定串行隊列延時操作

一次性執行

  • 程序中有的時候希望一個方法只被執行一次
一個方法只被執行一次
  • 讓dispatch_once執行在子線程上
讓dispatch_once執行在子線程上
  • dispatch_once是線程安全的
    • 多個線程的時候也是執行一次。
dispatch_once是線程安全的

調度組Dispatch Group

  • Dispatch Group調度組

    1. Dispatch Group會在被添加進調度組的所有任務都完成時通知你,這些任務可以是同步的,也可以是異步的,即便在不同的隊列也行。
    2. Dispatch Group調度組只有異步執行。
    3. 創建一個新的Dispatch Group,它的作用就像一個用于未完成任務的計數器。
    4. dispatch_group_enter手動通知Dispatch Group任務已經開始,必須保證dispatch_group_enterdispatch_group_leave成對出現,相當于確保進入Group的次數和離開Group的次數相等,否則可能會遇到詭異的崩潰問題。
    5. 包含不同隊列類型的調度組Dispatch Group
      • 自定義串行隊列:它很適合當一組任務完成時發出通知。
      • 主隊列(串行):它也很適合這種情況。但如果你要同步地等待所有工作地完成,就不應該使用,因為不能阻塞主線程,會導致死鎖。然而,主隊列的異步任務能用于在幾個較長任務完成后更新UI的方式。
      • 并發隊列:它也很適合Dispatch Group和完成時通知。
  • dispatch_group_wait

    1. 會阻塞當前線程,直到組里面所有的任務都完成或者等到某個超時完成。
    2. 如果在所有任務完成前超時了,該函數會返回一個非零值。可以對此返回值做條件判斷以確定是否超出等待周期。DISPATCH_TIME_FOREVER讓這個組永遠等待。
  • dispatch_apply

    1. dispatch_apply就像一個for循環,但它能并發地執行不同的迭代。
    2. 這個函數是同步執行的,所以和普通的for循環一樣,它只會在所有工作完成后才返回。
    3. 對于并發循環使用dispatch_apply,可以幫助你追蹤任務的進度。
  • dispatch_group_notify

    1. 以異步的方式工作。
    2. 不會阻塞任何線程。
    3. 有時候需要在多個異步任務都執行完成之后繼續做某些事情。
下載歌曲,等所有的歌曲都下載完畢之后轉到主線程提示用戶

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

推薦閱讀更多精彩內容

  • 1. GCD簡介 什么是GCD呢?我們先來看看百度百科的解釋簡單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 401評論 0 0
  • 多線程 在iOS開發中為提高程序的運行效率會將比較耗時的操作放在子線程中執行,iOS系統進程默認啟動一個主線程,用...
    郭豪豪閱讀 2,617評論 0 4
  • 本文首發于我的個人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「傳送門」[https...
    ITCharge閱讀 348,989評論 308 1,926
  • 從哪說起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個最簡單的問題,以這個作為切入點好了 在ma...
    Mr_Baymax閱讀 2,831評論 1 17
  • 我活著的目的,并不是為了取悅別人,也不是為了爭功奪力或者耀武揚威,之所以選擇如此的活著,不為其它,只為了更好的做真...
    肖先生肖軍閱讀 522評論 0 0