iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 29
0
Software Development

30天Swift入門學習系列 第 29

Multi-Thread handling - GCD(Grand Central Dispatch)

執行緒(thread)是 CPU 中真正來處理及執行指令的部分。以當前的 CPU 架構,每個核心內至少內含一個執行緒,如何新增執行緒來進行多工處理多個任務讓 App 執行順暢不會造成使用者在使用上有卡頓感,這是在撰寫 OS layer application 時必修的課題。

在 iOS 中,Apple 提供多個方法來實作多工任務,本篇來聊聊其中之一: GCD。
GCD 運作方式是採取資料結構中 Queue 的方式來排序待辦工作,並依照其特性 First in, first out 來安排一個 thread 處理這些已排程的工作任務。在使用 GCD 來進行多工處理時 iOS 會透過事先建立好的 thread pool 來安排工作任務會使用哪一個 thread 來處理,開發者毋須擔心在開發時要自行分配 thread。

使用 GCD 分派( dispatch )工作任務( tasks )時可將這些 tasks 全部安排至同一個 Queue 中或是多個 Queues 中,其行為分別稱做 serial 及 concurrent。建立方式如下:

// Serial Queue
let serialQueue: DispatchQueue = DispatchQueue(label: "serialQueue")
// Concurrent Queue
let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)

當 Queue 被建立並執行時, Queue 與 Queue 之間執行的方式分成了 Synchronous 及 Asynchronous。Synchronous 的意思是在一個時間點中只允許一個 Queue 執行,在其執行完所有的任務之前不允許其他的 Queue 進行動作,Asynchronous 則是允許多個 Queue 同時執行。
其使用方式如下:

serialQueue.async()
serialQueue.sync()

concurrentQueue.async()
concurrentQueue.sync()

Demo:
這個 demo 是在說明多工處理對 App 的影響。在此 App 中共有四個 ImageView ,按下 Start 後會從網路抓四張圖,並下於畫面底下有一 slider bar 可以拖動。

情況一: 將所有下載任務都放於 main thread 中處理。此時按下 start 開始下載並拖動 slider bar,會發現 slider bar 暫時無法拖動直到圖片下載完為止。

@IBAction func didClickOnStart(sender: AnyObject) {
  let img1 = downloadImageWithURL(url: imageURLs[0])
  self.imageView1.image = img1

  let img2 = downloadImageWithURL(url: imageURLs[1])
  self.imageView2.image = img2

  let img3 = downloadImageWithURL(url: imageURLs[2])
  self.imageView3.image = img3

  let img4 = downloadImageWithURL(url: imageURLs[3])
  self.imageView4.image = img4
}

情況二:使用 Serial dispatch 來處理圖片下載。此時按下 start 開始下載並拖動 slider bar,會發現圖片的顯示是一張一張依序出現,而 slider bar 可正常拖動。

@IBAction func didClickOnStart(sender: AnyObject) {
  let serialQueue: DispatchQueue = DispatchQueue(label: "serialQueue")

  serialQueue.async() { () -> Void in
    let img1 = self.downloadImageWithURL(url: self.imageURLs[0])
    DispatchQueue.main.async(execute: {
      self.imageView1.image = img1
    })
  }
  ...
}

情況三:使用 Concurrent dispatch 來處理圖片下載。此時按下 start 開始下載並拖動 slider bar,會發現圖片出現的時間是不規則且 slider bar 可正常拖動。

@IBAction func didClickOnStart(sender: AnyObject) {
  let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)

  concurrentQueue.async() {
    let img1 = self.downloadImageWithURL(url: self.imageURLs[0])
    DispatchQueue.main.async(execute: {
      self.imageView1.image = img1
    })
  }
  ...
}

Additional:
UIKit 內的所有 component 只能在 main thread 中更新,故要更新時需切換回 main thread。切換方法如下:

 DispatchQueue.main.async

Reference:
Source code on Github


上一篇
iOS App 實作(20) Face Detection
下一篇
Multi-Thread handling - Operation
系列文
30天Swift入門學習30

尚未有邦友留言

立即登入留言