Swift GCD (Grand Central Dispatch)

一個App如果完全是單線程開發的,那就意味著當有一段程序運行時間比較久的時候,就會造成主線程阻塞,也就導致了UI渲染過程會突然卡住的情況。比如說當我們進入一個畫面開始下載圖片的時候,如果圖片很大或者太多,就會造成等待的時候畫面直接不動了。這樣的設計會造成用戶體驗很差。

當一個App開啟進入運行狀態後就形成了一個進程。進程是系統進行資源分配和調度的一個單位,是一個運行中的程序。

一個進程可以有多個線程,而這些線程是共享這個進程的資源的。

iOS中提供了多種多線程的實現方式,這篇文章主要講GCD (Grand Central Dispatch)


iOS開發中常會遇到使用多線程(Multithreading),這裡想先從串行&併法(Serial & Concurrent)講起。

串行與並發

串行與並行

上圖所示,有兩種執行任務的方法,其中上面的我們成為串行(Serail),下面則是並發(Concurrent)

  • 串行的特徵:一個任務接著一個任務執行,在一個任務完成前,不會執行其他任務。
  • 併發的特徵:同時執行多個任務。

同步與異步

同步與異步

上圖中,左側為同步,右側為異步。

當Client端向Server端發送請求時(比如請求圖片),如果圖片很大,在圖片很大的情況下,就會需要進行一段時間的下載,而這個時候如果我們將下載圖片的任務放在主線程(main_queue)中執行的話,就會造成UI卡住不能動,直到下載完畢。

而如果我們採用右側的異步方式進行圖片下載,那麼UI就可以繼續進行渲染,當下在任務完成時,在進行圖片加載,不會阻塞到UI線程。

注:UI渲染是在主線程(main_queue)進行的。

隊列(Queue)

隊列同樣存在串行隊列與並行隊列,在隊列中的任務會依照隊列類型來執行任務(文中剛開始提到的,依次執行與同時執行任務)。

需要注意的是,當我們將隊列提交以後,GCD會負責找時間執行任務,唯一能保證的是,任務會按照隊列中的任務順序執行,但何時執行哪一個任務,是不知道的

其中一個特別的隊列叫做main_queue,是一種串行隊列,也是唯一可以更新UI的線程(thread)

而系統也提供了四種並行隊列Global Dispatch Queues,按照有線級別不同分為background, low, default, high

相關術語

  • 臨界區(Critical Section )
    同一段代碼不能被不同的線程同時執行,有時候當同一個資源被多個線程同時處理的話會造資源內容不可靠(比如銀行帳戶,同時多個款項進出)
  • 死鎖(Dead Lock)
    比如兩個線程要使用彼此所執行結果的內容,結果兩個線程都因對方沒辦法完成而導致自己無法完成,造成線程都卡住了。
  • 線程安全(Thread Safe)
    線程安全的Code可以被多個線程安全的調用而不會導致數據的不可靠或Crash。
  • Context Switch
    切換線程時儲存與恢復的執行狀態過程。

Code

創建隊列(Create Queue)

Global Dispatch Queue的四種級別:
  • DISPATCH_QUEUE_PRIORITY_HIGH  高
  • DISPATCH_QUEUE_PRIORITY_DEFAULT  正常
  • DISPATCH_QUEUE_PRIORITY_LOW  低
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND

任務

對列祖(Group)

其他功能

dispatch_apply,執行指定次數的任務,執行起來會和dispatch_sync一樣是串行的。

  • dispatch_group_async:用來監視一組任務是否執行完成
  • dispatch_group_notify:用來在所有任務完成後發出通知,不會阻塞當前線程
  • dispatch_group_wait:等待直到所有任務執行完畢才繼續執行後面的動作,會阻塞當前線程

互斥鎖

比如我們有一個轉帳功能的function叫做transferMoneySafely,我們先檢查是否有足夠的餘額進行轉帳,足夠的話就進行轉帳。

由於交易非常頻繁,可能我們進行轉帳的過程中,馬上又有轉帳的需求,在前一筆轉帳完成前,系統會誤以為餘額足夠,為了防止這樣的問題發生,就可以加入互斥鎖。

 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *