正在加载,请稍候…

Swift 并发实战:async/await、Actor 与结构化并发

掌握 Swift 并发编程,涵盖 async/await 语法、Actor 模型数据隔离、结构化并发任务组以及优雅的任务取消模式。

Swift 并发实战:async/await、Actor 与结构化并发

Swift 并发实战:async/await、Actor 与结构化并发

Swift 并发(从 Swift 5.5 引入,并在 Swift 6 中显著增强)提供了一种安全且富有表现力的模型来编写并发代码。回调金字塔和 DispatchQueue 的繁琐操作已成为过去。

基础:async/await

Swift 并发实战:async/await、Actor 与结构化并发 插图

定义和调用异步函数

func fetchUser(id: String) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw APIError.invalidResponse
    }

    return try JSONDecoder().decode(User.self, from: data)
}

从同步代码桥接

override func viewDidLoad() {
    super.viewDidLoad()
    Task {
        await loadProfile()
    }
}

func fetchImageFromLegacyAPI(_ url: URL) async throws -> UIImage {
    try await withCheckedThrowingContinuation { continuation in
        legacyImageLoader.load(url: url) { result in
            switch result {
            case .success(let image): continuation.resume(returning: image)
            case .failure(let error): continuation.resume(throwing: error)
            }
        }
    }
}

Actor 模型:安全的共享状态

Actor 通过在编译时隔离可变状态来防止数据竞争。

actor UserCache {
    private var cache: [String: User] = [:]
    private var fetchTasks: [String: Task<User, Error>] = [:]

    func user(for id: String) async throws -> User {
        if let cached = cache[id] { return cached }

        if let existingTask = fetchTasks[id] {
            return try await existingTask.value
        }

        let task = Task<User, Error> {
            try await APIClient.shared.fetchUser(id: id)
        }

        fetchTasks[id] = task

        do {
            let user = try await task.value
            cache[id] = user
            fetchTasks.removeValue(forKey: id)
            return user
        } catch {
            fetchTasks.removeValue(forKey: id)
            throw error
        }
    }

    func invalidate(id: String) { cache.removeValue(forKey: id) }
}

@MainActor

@MainActor
class ProfileViewModel: ObservableObject {
    @Published var user: User?
    @Published var isLoading = false
    @Published var error: Error?

    private let userCache = UserCache()

    func load(userId: String) async {
        isLoading = true
        defer { isLoading = false }

        do {
            user = try await userCache.user(for: userId)
        } catch {
            self.error = error
        }
    }
}

Swift 并发实战:async/await、Actor 与结构化并发 插图

结构化并发

并发 async let

func fetchAllUserData(userId: String) async throws -> UserDashboard {
    async let profile = APIClient.shared.fetchProfile(userId: userId)
    async let posts = APIClient.shared.fetchPosts(userId: userId)
    async let followers = APIClient.shared.fetchFollowers(userId: userId)

    return try await UserDashboard(
        profile: profile,
        posts: posts,
        followers: followers
    )
}

动态工作的 TaskGroup

func downloadImages(urls: [URL]) async throws -> [URL: UIImage] {
    try await withThrowingTaskGroup(of: (URL, UIImage).self) { group in
        for url in urls {
            group.addTask {
                let image = try await ImageLoader.shared.load(url: url)
                return (url, image)
            }
        }

        var results: [URL: UIImage] = [:]
        for try await (url, image) in group {
            results[url] = image
        }
        return results
    }
}

任务取消

Swift 并发实战:async/await、Actor 与结构化并发 插图

协作式取消

func processLargeDataset(_ items: [DataItem]) async throws -> [ProcessedItem] {
    var results: [ProcessedItem] = []

    for (index, item) in items.enumerated() {
        try Task.checkCancellation()

        let processed = try await processItem(item)
        results.append(processed)

        if index % 100 == 0 {
            await Task.yield()
        }
    }

    return results
}

SwiftUI 中的可取消视图任务

struct SearchView: View {
    @State private var query = ""
    @State private var results: [SearchResult] = []

    var body: some View {
        VStack {
            SearchBar(text: $query)
            ResultsList(results: results)
        }
        .task(id: query) {
            guard !query.isEmpty else { results = []; return }

            do {
                try await Task.sleep(for: .milliseconds(300))
                results = try await SearchService.search(query: query)
            } catch is CancellationError {
                // 忽略取消
            } catch {
                print("搜索错误:", error)
            }
        }
    }
}

AsyncSequence

func processEvents() async {
    let stream = AsyncStream<WebSocketMessage> { continuation in
        webSocket.onMessage = { message in
            continuation.yield(message)
        }
        webSocket.onClose = {
            continuation.finish()
        }
    }

    for await message in stream {
        await handleMessage(message)
    }
}

Swift 6 严格并发检查

// Sendable 一致性
struct UserData: Sendable {
    let id: String
    let name: String
}

// @unchecked Sendable 用于手动安全保证
final class ThreadSafeCounter: @unchecked Sendable {
    private var count = 0
    private let lock = NSLock()

    func increment() {
        lock.withLock { count += 1 }
    }

    var value: Int { lock.withLock { count } }
}

性能模式

限制并发以避免系统过载:

func batchProcess<T>(items: [T], maxConcurrency: Int = 4) async throws {
    try await withThrowingTaskGroup(of: Void.self) { group in
        var inFlight = 0

        for item in items {
            if inFlight >= maxConcurrency {
                try await group.next()
                inFlight -= 1
            }

            group.addTask { try await processItem(item) }
            inFlight += 1
        }

        try await group.waitForAll()
    }
}

结论

Swift 并发改变了 iOS 和 macOS 开发。async/await 语法消除了回调金字塔,Actor 在编译时防止数据竞争,结构化并发使并行工作安全且可读。借助 Swift 6 的严格检查,编译器成为你的并发安全网,使代码更易于推理、更安全,并且通常更快。