Swift 執行緒與並發程式設計
📋 目錄
🔹 執行緒基礎概念
Main Thread (主執行緒)
- 職責:負責 UI 操作(顯示畫面、處理觸控事件)
- 規則:任何 UI 更新 必須 在主執行緒上執行
- 注意:避免在主執行緒執行耗時工作,會導致 App 卡住
Background Threads (背景執行緒)
- 用途:處理耗時工作(網路請求、檔案讀寫、圖片處理)
- 流程:背景處理完成 → 切回主執行緒 → 更新 UI
🔹 UI 更新原則
⚠️ 重要:UI 更新只能在主執行緒 (Main Thread) 上進行
原因:UIKit / SwiftUI 不是 Thread-safe,在背景執行緒直接修改 UI 會導致錯誤或閃退。
🔹 實務應用方法
方法一:使用 DispatchQueue
// 在背景執行緒處理耗時工作
DispatchQueue.global().async {
let data = downloadData() // 模擬耗時工作
// 回到主執行緒更 新 UI
DispatchQueue.main.async {
self.label.text = "下載完成"
}
}
方法二:搭配 async/await
func fetchData() async -> String {
try? await Task.sleep(nanoseconds: 1_000_000_000)
return "資料下載完成"
}
func loadData() {
Task {
let result = await fetchData()
// 在主執行緒更新 UI
await MainActor.run {
self.label.text = result
}
}
}
方法三:使用 @MainActor
@MainActor
func updateUI(text: String) {
self.label.text = text
}
// 或者標註整個 class
@MainActor
class ViewController: UIViewController {
// 所有方法都會在主執行緒執行
}
🔹 Swift Concurrency 關鍵字
1. async/await
Swift 5.5 引入的並發模型核心語法:
async:標記非同步函式await:等待非同步函式完成,不阻塞執行緒
func fetchData() async -> String {
try? await Task.sleep(nanoseconds: 1_000_000_000)
return "資料下載完成"
}
func load() async {
let result = await fetchData()
print(result) // 資料下載完成
}
2. @MainActor
確保程式在主執行緒執行,常用於 UI 更新:
@MainActor
func updateUI() {
// 一定會在主執行緒上執行
print("更新 UI")
}
// 搭配 async 使用
func loadData() async {
let result = await fetchData()
await MainActor.run {
print("更新畫面: \(result)")
}
}
3. @preconcurrency
用於與舊程式碼相容性:
@preconcurrency protocol MyLegacyProtocol {
func doSomething()
}
用途:讓舊的 API 可以在新的 Concurrency 環境下被接受,主要是過渡性工具。
🔹 最佳實踐
執行緒使用原則
- 主執行緒:專門負責 UI 更新和事件處理
- 背景執行緒:處理耗時工作
- 切換規則:背景工作完成 → 主執行緒更新畫面