Swift Concurrency并发
Swift Concurrency并发

Swift Concurrency并发

Swift Concurrency并发是Apple在 Swift 5.5(随 iOS 15 / macOS 12 引入)中推出的一套现代并发编程模型,核心是以结构化并发为基础的 async/await 语法、Task 和 Actor 等组件。相比传统的 GCD,它更安全、更可读、更易于维护。

Swift并发编程的发展演进

Swift Concurrency 引入之前(Swift < 5.5)

时间点:Swift 初期(iOS 4 时代开始,一直沿用至今)

特点

1、使用 DispatchQueue.async 实现异步操作。

2、底层基于 C 的 API,灵活但易出错。

3、手动线程切换,嵌套回调严重。

代码示例

DispatchQueue.global().async {
    // 假设这个是耗时操作,如网络请求
    let data = fetchData()
    DispatchQueue.main.async {
        self.label.text = data.title
    }
}

问题

1、容易嵌套多层 callback(回调地狱)。

2、容易忘记切回主线程导致 UI 崩溃。

3、错误传播复杂。

4、很难读出任务执行顺序。

Swift Concurrency 引入之后(Swift 5.5+)

时间点:Swift 5.5(iOS 13+/macOS 10.15+)起,正式稳定

核心特性:

1、async/await 简化异步操作。

2、Task 任务系统,结构化并发。

3、actor 实现数据并发隔离(取代传统锁)。

4、自动线程跳转(如主线程更新 UI)。

代码示例

func loadData() async {
    let (data, _) = try await URLSession.shared.data(from: url)
    let model = try JSONDecoder().decode(MyModel.self, from: data)
    self.label.text = model.name
}

优势

1、写法直观,无需嵌套。

2、错误处理统一 try/await。

3、自动线程管理,更安全。

4、容易维护、测试。

核心概念

1、async / await

用于声明异步函数并等待其完成,避免“回调地狱”。

func fetchUserData() async throws -> User {
    // 异步网络请求
}

Task {
    do {
        let user = try await fetchUserData()
        print(user)
    } catch {
        print("Error:", error)
    }
}

通过使用 async 和 await,可以编写非阻塞的代码,处理需要等待完成的任务,比如网络请求、文件读写等。避免使用复杂的回调或闭包,代码变得更加直观和易于理解,相关文章《Swift UI 深入理解async和await》。

2、Task

用于启动一个新的并发任务。

Task {
    await doSomething()
}

可以设置优先级:

Task(priority: .high) {
    await fetchData()
}

也可以取消:

let task = Task {
    try await longRunningTask()
}

task.cancel()

Task 是一种用于启动、管理和控制异步操作的轻量级结构。它不是线程,而是运行在 Swift 并发运行时中的“协程任务”。相关知识请见《Swift并发模型中的Task》。

3、TaskGroup 和 async let

用于并发执行多个异步任务并聚合结果。

async let(结构化并发):

async let image1 = loadImage(from: url1)
async let image2 = loadImage(from: url2)

let (img1, img2) = await (image1, image2)

TaskGroup(动态并发任务):

func fetchAll() async throws -> [Data] {
    try await withThrowingTaskGroup(of: Data.self) { group in
        for url in urls {
            group.addTask {
                try await fetch(url)
            }
        }

        var results: [Data] = []

        for try await data in group {
            results.append(data)
        }

        return results
    }
}

TaskGroup 和 async let 都是用于并发并行执行多个异步任务的工具,但它们各自适用于不同的场景。

async let:并发启动多个已知数量的异步任务,适合数量固定、结构清晰的并发。

TaskGroup:动态创建多个异步任务并等待全部完成,适合数量不定、动态生成的任务。

更多知识详见《Swift并发模型中的TaskGroup和async let》。

4、Actor

用于保护共享状态,避免数据竞争(数据一致性保证)。

actor Counter {
    private var value = 0
    
    func increment() {
        value += 1
    }

    func getValue() -> Int {
        value
    }
}

let counter = Counter()
await counter.increment()
let val = await counter.getValue()

Actors和Class都是引用类型,与Class类似,但是Actor主要用于保护共享状态的并发常见。

因此,只有在需要多线程访问同一个对象时,才使用Actor。

更多知识详见《Swift并发模型中的Actor》。

主线程执行 (MainActor)

@MainActor
class ViewModel: ObservableObject {
    @Published var text = ""

    func updateText() {
        text = "Updated"
    }
}

或用于函数:

@MainActor
func updateUI() {
    label.text = "Done"
}

这里的text赋值都涉及到UI操作,因此需要在方法或者类的顶部声明@MainActor,将代码操作切换回主线程。相关文章请见《Swift主线程标记@MainActor

异步上下文

在 async 函数或 Task 中执行代码时,就处于“异步上下文”中。

具体来说,以下情况是“异步上下文”:

1、async 函数体内:

func fetchData() async {
    // 这里是异步上下文
}

2、Task 或 Task.detached 中的闭包:

Task {
    // 这里是异步上下文
}

3、await 后续的代码:

func someAsyncFunction() async {
    await doSomething()
    // 这里仍是异步上下文
}

Swift 6 开启了“严格的并发检查”,在异步上下文中不能随意访问主线程相关的内容(例如 Thread.isMainThread)。

DispatchQueue.global().async {
    print("现在在哪?", Thread.isMainThread) // false

    Task {
        print("Task 在哪?", Thread.isMainThread) // 报错提示行
    }
}

会输出:

Class property 'isMainThread' is unavailable from asynchronous contexts; Work intended for the main actor should be marked with @MainActor; this is an error in the Swift 6 language mode

异步上下文可能不在主线程上,访问主线程资源是不安全的,编译器会报错。

因此,应该使用 @MainActor 或 MainActor.run:

Task {
    await MainActor.run {
        print("这里安全地在主线程中运行")
    }
}

Concurrency本质

Swift Concurrency 是一整套并发运行时 + 类型安全机制

1、Swift 运行时中引入了协作式任务调度(Cooperative Task Scheduling)

并不是用传统线程池(如 GCD),而是使用更轻量的任务(Task)调度器。

调度器运行在少量线程上,会在挂起点 await 自动让出资源,提升并发效率。

优势是节省线程开销,性能更高,避免线程爆炸。

2、引入结构化并发(Structured Concurrency)

每个 Task 是有父子层级关系的,自动管理生命周期。

自动传播 cancel、error,避免资源泄漏。

这是现代语言并发设计的核心,比如 Kotlin Coroutine、Rust Future 都有类似概念。

3、类型系统中新增 Sendable / @Sendable

编译器会检查在并发中共享的数据是否安全。

防止并发线程中访问非线程安全对象。

Actor 内部隔离状态,同时让类型系统保证并发安全。

4、Actor 实际上封装了一个串行任务队列

Actor 就像“线程安全类”,发消息(调用方法)给它,它自动按顺序执行。

替代了锁(mutex、semaphore),并发逻辑更简洁安全。

Swift 编译器确保只能通过异步方法访问 Actor 的内部状态。

5、引入隔离域(Isolation Domain)

比如 @MainActor 是一个特殊的隔离域,代码在主线程上串行运行。

编译器会自动插入线程切换逻辑,防止错误线程访问 UI。

所有 Actor 都运行在各自的隔离域中,隔离的数据访问。

总结

Swift Concurrency 是语言级的并发模型,结合语法、调度器、类型系统和运行时,构建了安全、高效、可读的异步并发执行体系。

扩展知识

async/await和“回调地狱”

在正文中说到async/await 避免了“回调地狱”,是因为它彻底改变了异步代码的写法,让“同步的方式编写异步逻辑”,不再陷入一层层嵌套的回调结构。

什么是“回调地狱”?

“回调地狱”通常长这样:

loadUser { user in
    fetchPosts(for: user) { posts in
        downloadImages(for: posts) { images in
            updateUI(with: images)
        }
    }
}

1、每一层都是一个闭包(callback)。

2、缩进越来越深,可读性变差。

3、错误处理复杂(每层都要处理)。

4、难以写“顺序逻辑”,比如:加载 → 解析 → 展示。

回调地狱本质上是“异步调用的嵌套”结构。常见于:

1、网络请求完成后通知。

2、文件下载完成后通知。

3、延迟执行后的处理。

4、等待某个事件触发再执行处理逻辑。

async/await 的写法

使用 async/await,你可以把上面的代码重写为:

func loadAll() async {
    let user = await loadUser()
    let posts = await fetchPosts(for: user)
    let images = await downloadImages(for: posts)
    updateUI(with: images)
}

看起来像是“同步写法”,但执行依然是异步的:

1、代码结构扁平、清晰

2、按照自然的顺序思考逻辑

3、错误处理也统一、简洁(可用 do-catch)

await 会在编译时被转成状态机,每个 await 之后的代码被保存在 continuation 中(下一步执行),这样可以暂停当前函数,等结果回来后自动恢复继续运行。

async/await 只能用来调用异步函数。

相关文章

1、Swift管理多线程任务框架GCD:https://fangjunyu.com/2024/11/02/swift%e7%ae%a1%e7%90%86%e5%a4%9a%e7%ba%bf%e7%a8%8b%e4%bb%bb%e5%8a%a1%e6%a1%86%e6%9e%b6gcd/

2、Swift UI 深入理解async和await:https://fangjunyu.com/2024/10/12/swift-ui-%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3async%e5%92%8cawait/

3、Swift主进程:https://fangjunyu.com/2024/12/25/swift%e4%b8%bb%e7%ba%bf%e7%a8%8b/

4、Swift并发模型中的Task.result属性:https://fangjunyu.com/2024/12/09/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84task-result%e5%b1%9e%e6%80%a7/

5、Swift并发模型中的Task:https://fangjunyu.com/2025/05/15/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84task/

6、Swift主线程标记@MainActor:https://fangjunyu.com/2024/12/25/swift%e4%b8%bb%e7%ba%bf%e7%a8%8b%e6%a0%87%e8%ae%b0mainactor/

7、Swift并发模型中的MainActor.run方法:https://fangjunyu.com/2025/05/15/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84mainactor-run%e6%96%b9%e6%b3%95/

8、Swift并发模型中的TaskGroup和async let:https://fangjunyu.com/2025/05/15/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84taskgroup%e5%92%8casync-let/

9、Swift并发模型中的Actor:https://fangjunyu.com/2024/10/22/swift%e7%a7%91%e6%99%ae%e6%96%87%e3%80%8aactor-%e9%9a%94%e7%a6%bb%e3%80%8b/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注