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 更新和事件處理
- 背景執行緒:處理耗時工作
- 切換規則:背景工作完成 → 主執行緒更新畫面
推薦做法
- 新專案優先使用
async/await+@MainActor - 舊專案可繼續使用
DispatchQueue - 避免在主執行緒執行耗時操作
- 使用
@preconcurrency處理相容性問題
常見錯誤
- ❌ 在背景執行緒直接更新 UI
- ❌ 在主執行緒執行網路請求
- ❌ 忘記切回主執行緒更新畫面
📊 執行緒流程圖
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 使用者操作 │───▶│ 主執行緒 │───▶│ UI 更新 │
│ 點擊按鈕 │ │ (處理事件) │ │ *顯示結果* │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ 背景執行緒 │
│ 下載資料 │
└──────────────────┘
│
▼
┌──────────────────┐
│ 切回主執行緒 │
│ 更新 UI │
└──────────────────┘