结构化并发(Structured Concurrency)

结构化并发是 Swift 5.5+ 引入的核心并发模型,简单来说:它让异步任务的生命周期与代码的语法结构严格绑定,父任务能管控子任务的创建、执行和销毁,避免"游离任务"导致的内存泄漏、崩溃或资源泄漏

对比传统的"非结构化并发"(如 GCD 分散的 async 调用、闭包回调),结构化并发就像"有组织的团队协作"------父任务是管理者,子任务是团队成员,成员的工作范围、生命周期完全由管理者掌控;而非结构化并发则是"散兵游勇",任务创建后脱离管控,容易出现混乱。

一、核心特征(结构化 vs 非结构化)

1. 非结构化并发的问题(GCD/闭包)

swift 复制代码
// 非结构化:任务创建后脱离管控,父函数返回后子任务仍可能执行
func unstructuredTask() {
    // 子任务1:游离在外,无法被父函数管控
    DispatchQueue.global().async {
        Thread.sleep(forTimeInterval: 10) // 长时间执行
        print("子任务1执行(可能父函数已返回)")
    }
    // 子任务2:独立于子任务1,无统一生命周期
    DispatchQueue.global().async {
        print("子任务2执行(无法保证与子任务1的顺序)")
    }
}
// 调用后父函数立即返回,子任务仍在后台执行,易导致资源泄漏/数据竞争
unstructuredTask()

2. 结构化并发的核心规则

结构化并发遵循 3 个核心原则,确保任务"可管可控":

  • 父子关联 :子任务必须由父任务创建(如 TaskGroup),父任务会等待所有子任务完成后才结束;
  • 作用域绑定 :任务的生命周期被限制在明确的代码块内(如 withTaskGroup 的闭包),超出作用域自动终止;
  • 统一取消:取消父任务时,所有子任务会被自动取消,避免"僵尸任务";
  • 清晰的层级:任务的执行顺序、依赖关系通过代码结构体现,而非隐藏在闭包/回调中。

二、Swift 中结构化并发的核心实现

1. 基础:Task 的结构化使用

单个 Task 本身可以是"结构化"的------当 Task 被创建在另一个异步上下文中时,它会成为父任务的子任务,继承生命周期:

swift 复制代码
func structuredSingleTask() async {
    // 子任务:绑定到当前异步上下文(父任务)
    let childTask = Task {
        await Task.sleep(1_000_000_000)
        print("子任务执行(父任务未完成前不会游离)")
    }
    // 父任务等待子任务完成(结构化的核心体现)
    await childTask.value
    print("父任务完成(子任务已执行完毕)")
}

// 调用:父任务的生命周期管控子任务
Task {
    await structuredSingleTask()
}

2. 核心:TaskGroup(多任务结构化管理)

TaskGroup 是结构化并发的核心工具,用于管理一组子任务,确保所有子任务完成后父任务才继续执行:

swift 复制代码
/// 结构化并发:批量下载图片(TaskGroup 管控所有子任务)
func downloadImagesStructured(urls: [String]) async -> [Data] {
    // withTaskGroup 定义任务组的作用域,所有子任务绑定在此作用域内
    return await withTaskGroup(of: Data.self) { group in
        // 1. 提交所有子任务(子任务属于该任务组)
        for url in urls {
            group.addTask {
                // 模拟图片下载
                await Task.sleep(500_000_000)
                return "Image data for \(url)".data(using: .utf8)!
            }
        }
        
        // 2. 收集子任务结果(父任务会等待所有子任务完成)
        var images = [Data]()
        for await imageData in group {
            images.append(imageData)
        }
        
        // 3. 作用域结束:所有子任务已完成,无游离任务
        return images
    }
}

// 调用:结构化执行,无游离任务
Task {
    let urls = ["url1", "url2", "url3"]
    let images = await downloadImagesStructured(urls: urls)
    print("所有图片下载完成:\(images.count)张")
}

3. 进阶:取消与异常的结构化处理

结构化并发的关键优势是统一的取消和异常处理------父任务可取消所有子任务,子任务抛出的异常会向上传递:

swift 复制代码
func structuredCancellation() async {
    await withTaskGroup(of: Void.self) { group in
        // 子任务1:长时间执行
        group.addTask {
            for i in 1...10 {
                // 检查是否被取消(结构化取消的核心)
                try Task.checkCancellation()
                await Task.sleep(100_000_000)
                print("子任务1执行第\(i)次")
            }
        }
        
        // 子任务2:触发取消
        group.addTask {
            await Task.sleep(300_000_000)
            group.cancelAll() // 取消所有子任务
            print("子任务2:取消所有任务")
        }
        
        // 等待所有子任务(包括被取消的)
        await group.waitForAll()
    }
    print("任务组执行完毕(所有子任务已终止)")
}

// 调用:取消后子任务1会停止,无资源泄漏
Task {
    await structuredCancellation()
}

三、结构化并发的核心价值

1. 避免"僵尸任务"

非结构化并发中,父函数返回后子任务可能仍在执行(如 GCD 的 async),导致:

  • 内存泄漏(子任务持有外部对象);
  • 资源浪费(无效的网络请求/计算);
  • 崩溃(子任务修改已释放的对象)。
    结构化并发中,任务生命周期绑定到代码结构,超出作用域自动终止,彻底解决此问题。

2. 简化错误处理

子任务抛出的异常会自动向上传递到父任务,无需在闭包中分散处理错误:

swift 复制代码
func structuredErrorHandling() async throws {
    try await withThrowingTaskGroup(of: Data.self) { group in
        group.addTask {
            throw NSError(domain: "DownloadError", code: -1) // 子任务抛异常
        }
        
        // 异常会自动向上传递,父任务可统一捕获
        for try await _ in group {}
    }
}

// 统一捕获错误
Task {
    do {
        try await structuredErrorHandling()
    } catch {
        print("捕获异常:\(error)")
    }
}

3. 提升代码可读性

结构化并发让异步任务的依赖、顺序通过代码缩进/结构体现,而非隐藏在闭包嵌套中:

  • 非结构化:回调嵌套(回调地狱),任务关系不直观;
  • 结构化:线性代码,任务的父子、顺序关系一目了然。

四、结构化 vs 非结构化任务的对比

特性 结构化并发(TaskGroup/结构化Task) 非结构化并发(GCD/非结构化Task)
任务生命周期 绑定代码结构,可管控 游离在外,无统一管控
取消机制 父任务可取消所有子任务 需手动管理取消标记
错误处理 异常自动向上传递,统一捕获 错误分散在闭包中,易遗漏
资源泄漏 几乎无(作用域结束自动清理) 高(任务持有外部对象)
代码可读性 高(线性结构) 低(回调嵌套)

总结

  1. 结构化并发的核心是任务生命周期与代码结构绑定,父任务管控子任务的创建、执行、取消,避免游离任务;
  2. Swift 中通过 TaskGroup(多任务)、结构化 Task(单任务)实现结构化并发,替代传统的 GCD 非结构化调用;
  3. 核心价值:解决非结构化并发的资源泄漏、错误处理混乱问题,同时提升代码可读性和可维护性;
  4. 适用场景:所有需要管理多个异步任务的场景(如批量网络请求、并行计算),是 Swift 并发模型的核心推荐范式。
相关推荐
2501_916008896 分钟前
iOS应用开发工具全面解析:如何选择与优化开发效率
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
a44931536216 分钟前
iPhone Face ID 故障的分层诊断——原深感系统与主板加密配对机制
macos·ios·电脑·cocoa·iphone
萧瑟余晖26 分钟前
JDK 26 新特性详解
java·开发语言
白玉cfc1 小时前
【iOS】内存五大分区
macos·ios·cocoa
马优晨1 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区3 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大3 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai4 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜4 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa4 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio