談談程式 4 — 淺談 GCD (Grand Central Dispatch )

Shien
彼得潘的 Swift iOS / Flutter App 開發教室
7 min readAug 11, 2022

為了讓iOS使用者擁有更快速的使用體驗,我們應用程式設計師必須讓程式跑得更快。幸好我們不需要從頭煩惱怎麼變快這件事,因為 Apple 在 Mac OS X 10.6 時釋出了這項功能,系統在 iOS4 以上都能使用。

GCD 是設計用來優化應用程式系統,搭配多核心處理器讓程式以平行的方式再多執行序進行,比起以往一個一個執行快了好幾倍。

GCD 提供開發者 Dispatch Queue 等的 API ,讓我們不需要直接管理 thread ,就可以有效地安排任務給 GCD 處理。

Dispatch Queue

Dispatch Queue 是 GCD 其中一個強大的工具,他可以承載我們安排的任務( task ),這邊的任務也就是一個區塊的程式碼。

把 qeueu 想像成一個輸送帶,這些輸送帶會自動將我們派發的任務分給既有的執行續 (thread)。

Queue 遵從 FIFO 原則 (First In First Out),代表放進的第一個任務會被第一個拿出來完成。

然而 Dispatch Queue 會有不同的派遣模式,分為兩種 — Serial 及 Concurrent。

Serial Dispatch Queue

從他的名字得知,該 queue 在派遣任務時,下一個任務必須等待上一個任務執行完畢才會開始執行。假如任務一尚未結束,任務二就會持續等待。

換成程式碼來看

建立一個 serial 的 dispatch queue,按照 task 順序每個 task 印三次

task 2 會乖乖地等 task 1 全部印完後才執行

Concurrent Dispatch Queue

但通常我們會想要讓任務更快全部完成,如果能夠一心多用當然比一個一個做還快。Concurrent 的 Dispatch Queue 可以實現平行做工的功能。不需要等派遣出的第一個任務結束,任務二就可以開始執行,任務三當然也不用等任務一跟任務二執行結束才開始。

用程式來看

在宣告 dispatch queue 時多設定一個叫 attributes 的參數,選擇 concurrent。可以看到在 queue裡 task1 是排在 task 2 之前面的。

但印出來的結果是無法預期的,因為兩個任務是同時並行的。

我故意讓 task 2 只印兩次,可以發現任務而早早執行也早早結束了。

Main Queue

通常在執行專案時,會有個 main thread,main thread 是處理所有跟 UI 有關係的工作。而 main thread 也有特定的 queue — Main Queue。擺在 main queue 的任務基本上都會跟 UI 有關係,為了不讓使用者操作介面時有卡頓的感覺,不會把花時間的任務擺在 main queue 裡。因為這個原因, main queue 都是默認為最優先處理的 queue 。

用程式來看

在 concurrent queue 前後各加上 start & end,此時這兩個 print 工作都是在 main queue 裡的。

可以看到,start 跟 end 會先結束,再來才是 task 1及 task 2。代表 main queue 是優先處理的。

Asynchronize and Synchronize

在剛剛的程式範例中可以看到都是使用 async 來執行。這邊的 async 代表其他任務不必等前一項任務回傳時才執行,sync 則表示必須等待前面的任務回傳後,才能執行下一個任務。

Asynchronize

這次利用 concurrent queue 來實作,增加一個 task 3,按照順序寫

因為 concurrent 的關係,所以 task 1 不是第一個執行的,由於 async 的關係,task1 跟 task2 在 task 3 結束前就執行了。

Synchronize

這次將 async 全改成 sync ,一樣按照順序寫

可以看到雖然是 concurrent ,但必須符合 sync 規則,task 2 要等 task 1回傳(跑完)才能執行,task 3 要等 task 的 回傳(跑完)才能執行。

Qos (Quality of Service)

qos 層級圖

簡而言之,qos 就是控制 qeuue 之間的優先順序。隨著任務遞增,queue 有可能也會增加,有時會想要將一些任務提前完成,這時 qos 就派上用場了。

上圖為 qos 的層級,越上面的 queue 越優先得被執行。通常沒有特別註記的 queue 都是 default 層級。

用程式來看

可以看到 queue1 的 qos 為最高層、queue2 的 qos 為最底層,

雖然都是 concurrent 且 queue2 寫在 queue1 前面,由於 qos 的關係,queue 1 的 task 都優先被處理。

現在把 queue2 的 qos 改為 default

有看到 queue 2 的任務被關心到了,但還是沒有 queue1 的來的優先處理。

除了硬體上的改變、晶片上的進化,我們工程師也要跟上腳步來迎合這些工具,除了讓使用者體驗更好外,也是告訴自己科技是沒有停下腳步的一天。

從以往的一心一用轉化爲一心多用,慢就輸了!

--

--