Swift并发模型中的TaskGroup和async let
Swift并发模型中的TaskGroup和async let

Swift并发模型中的TaskGroup和async let

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

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

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

async let

并发执行多个已知任务。

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

let result = try await [image1, image2, image3]

另一种写法:

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

let result1 = try await image1
let result2 = try await image2
let result3 = try await image3

特点

启动三个异步任务并发执行,而不是顺序。

最终使用 await 等待所有任务结果。

适合任务数量确定且类型一致(如并发加载多个图片)。

注意:不能用for循环(数量必须提前写死。)

TaskGroup

动态并发多个任务。

func fetchAllImages(urls: [String]) async throws -> [UIImage] {
    try await withThrowingTaskGroup(of: UIImage.self) { group in
        for url in urls {
            group.addTask {
                try await loadImage(from: url)
            }
        }

        var results: [UIImage] = []

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

        return results
    }
}

特点

可在运行时动态添加任务。

自动并发执行。

支持错误处理(withThrowingTaskGroup)。

比 async let 更灵活,适合处理不定数量的异步任务(如循环、分页、动态生成内容)。

代码解析

1、withThrowingTaskGroup(…):创建一个任务组(所有任务一起并发执行,支持抛错)。

2、for url in urls:动态遍历 URL,可以是 3 个、10 个、100 个都行。

3、group.addTask { … }:每次调用 addTask,就创建一个任务。任务立即启动,最终会返回一个UIImage。

4、for try await image in group:一个个收集结果,哪个任务先完成,就先得到哪个结果,其中image是任务返回的值。

5、results.append(image):把下载好的图片收集到数组中。

async let 和 TaskGroup的区别

async let 和 TaskGroup 都能实现并发执行多个任务,但是在设计目的、能力范围和使用场景上有明显区别。

1、不固定数量任务:async let不支持且不能循环,TaskGroup支持并且可以循环。

// async let 不支持动态循环
for url in urls {
    async let image = downloadImage(from: url) // 错误 
}

// TaskGroup 支持
for url in urls {
    group.addTask {
        try await downloadImage(from: url)
    }
}

2、任务控制能力:async let不支持添加任务时做逻辑判断,不支持添加任务时动态设置参数,不支持在子任务里处理错误,TaskGroup具有任务控制能力,可以添加任务并判断、记录和处理异常。

3、错误处理:async let支持错误传播,但不能捕获局部错误。当整个await抛出错误时,不会处理其他任务。TaskGroup支持错误传播和捕获局部错误。还可以处理每个任务抛出的错误,灵活控制。

// async let:某个任务失败,整体 throw,没法控制哪个失败
async let a = try mayFail1()
async let b = try mayFail2()
let result = try await [a, b]

// TaskGroup:每个任务都可单独 try-catch
group.addTask {
    do {
        return try await mayFail()
    } catch {
        print("一个任务失败了,但继续")
        return defaultValue
    }
}

4、性能优化:async let在声明后立即开始,TaskGroup在addTask时立即开始。

async let a = fetchData1()  // 执行并启动任务
async let b = fetchData2()  // 执行并启动任务
async let c = fetchData3()  // 执行并启动任务

上面这三行代码,每一行一执行就启动任务,而不是等到 await 的时候才开始执行。

三个任务是并发执行的,只是稍后再取结果。

try await withThrowingTaskGroup(of: Int.self) { group in
    group.addTask { try await fetchData1() }
    group.addTask { try await fetchData2() }
    group.addTask { try await fetchData3() }
}

这里任务在调用group.addTask { … } 的时候就立刻开始执行了。

两者在任务开始时机上是一样的,都是定义时立刻启动。

区别主要是:

async let 是语法上的声明。

TaskGroup 可以在任意位置调用 addTask,甚至放进 for 循环里动态控制。

5、返回结构顺序:async let按声明顺序固定,TaskGroup按任务完成顺序,如果需要保留顺序,需要自己记录index。

async let a = fetch1()
async let b = fetch2()
async let c = fetch3()

let result1 = try await a  // 对应 fetch1()
let result2 = try await b  // 对应 fetch2()
let result3 = try await c  // 对应 fetch3()

顺序始终一致,结果也一一对应。

try await withThrowingTaskGroup(of: String.self) { group in
    group.addTask { try await fetch("A") }
    group.addTask { try await fetch("B") }
    group.addTask { try await fetch("C") }

    var results: [String] = []

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

    return results
}

这里的 result 是哪个任务先完成就先返回,不是 addTask 的顺序。

比如写了 A→B→C,结果可能是 B 最快返回,顺序就变成:

["B 的结果", "A 的结果", "C 的结果"]

如果想要在TaskGroup中保证顺序,可以在addTask里手动标记顺序:

group.addTask {
    let result = try await fetch("A")
    return (index: 0, value: result)
}

然后最后排序:

results.sort { $0.index < $1.index }

注意事项

async let 不能放在循环或条件语句中(因为数量必须在编译时固定):

// 错误:不能在 for 中用 async let
for url in urls {
    async let img = loadImage(from: url)
}

TaskGroup 不返回顺序结果,结果的顺序由任务完成顺序决定,如果需要有序结果,需额外处理。

   

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

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

发表回复

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