结构化并发(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 并发模型的核心推荐范式。
相关推荐
OKkankan1 小时前
红黑树的原理及实现
开发语言·数据结构·c++·算法
Bert.Cai2 小时前
Python time.sleep函数作用
开发语言·python
lxl13072 小时前
C++算法(11)字符串
开发语言·c++·算法
陳10302 小时前
C++:哈希表
开发语言·c++·散列表
稻草猫.2 小时前
SpringBoot日志全解析:从调试到持久化
java·开发语言·spring boot·java-ee·idea
小鸡吃米…2 小时前
Python线程同步
开发语言·数据结构·python
白帽子黑客-宝哥2 小时前
渗透测试“保姆级”实战成长手册
开发语言·网络安全·渗透测试·php
脑子不好真君3 小时前
手势操控的粒子土星 (Three.js + MediaPipe)
开发语言·javascript·ecmascript
LXS_3573 小时前
案例 —— 机房预约系统
开发语言·c++·学习方法