Swift Task 结构化并发

结构化并发: 一种组织并发任务的编程范式,强调任务之间的明确父子关系和生命周期管理,任务形成明确的父子关系树,父任务取消会自动传播到所有子任务, 作用域结束时也会自动等待所有子任务完成

非结构化并发: 更自由的并发任务管理,需要各自管理生命周期,可以存储任务引用,更加灵活

Swift并发中,结构化并发的另种方法: task group & async let

1. TaskGroup 话不多说上代码

swift 复制代码
struct TestTaskGroup {
    func test() async {
        print("Start")
        let result = try? await withThrowingTaskGroup(of: Int.self) { group in
            for i in 0 ..< 10 {
                group.addTask(priority: .high) {
                   let result = try await calculate(i)
                    return result
                }                
            }
            print("Task added")
            for try await result in group {
                print("Get result:\(result)")
            }
            print("Task ended")
            return value
        }
        print("End")
    }
    
    private func calculate(_ value: Int) async throws -> Int {
        print("Start calculate\(value)")
        try await Task.sleep(nanoseconds: UInt64(value * 5) * NSEC_PER_SEC)
        print("任务是否被取消 \(try Task.isCancelled)")
        print("calculate\(value) done")
        return value
    }
}

log输出:

可以看到任务在调用test的时候已经开始了 并且是并发执行的,group中的每个子任务都拥有返回值,并且group子任务返回值类型是一致的这点很有限制相对于async let 这点之后会讲, 如果我们想获取到所有子任务值之后应该怎么做呢 我们来改动一下代码如图:

这里上传了图1和图2 强调一下图1这种实现是不安全的 不推荐的

如上图我们所写要求group中的返回值是统一的例子中都是Int, 另外如果任何一个子任务抛出异常 整个taskGroup都会异常,所以一定要注意单个异常的捕获 如下异常处理:

swift 复制代码
private func calculate(_ value: Int) async throws -> Int {

        print("Start calculate\(value)")

        try? await Task.sleep(nanoseconds: UInt64(value * 2) * NSEC_PER_SEC)

        if value == 2 {

            print("Work\(value) throwing error")

            throw NSError(domain: "com.example.error", code: 1, userInfo: [NSLocalizedDescriptionKey: "Task \(value) failed"])

        }

        print("calculate\(value) done")

        return value

    }
    
    func calculateTest() async {

        print("Start")

        do {

            let result = try await withThrowingTaskGroup(of: Int.self) { group in

                var value = 0

                for i in 0 ..< 3 {

                    group.addTask(priority: .high) {

                        do {

                            let result = try await calculate(i)

                            return result

                        } catch {

                            print("Error while processing results: \(error)")

                            throw error  // 重新抛出以取消整个任务组

                        }

                    }

                    

                }

                print("Task added")

                

                do {

                    for try await result in group {

                        value += result

                        

                        print("Get result:\(result) value is \(value)")

                    }

                } catch {

                    print("Error while processing results: \(error)")

                    throw error

                }

                

                print("Task ended")

                return value

            }

            print("End result is \(result)")

        } catch {

            print("Caught error: \(error)")

        }

    }

2. async let 是另外一种创建结构化并发子任务的方式,他的返回值类型不必是同样的类型,提高了灵活性

swift 复制代码
func asyncLet() async throws {
        print("Program started")
        async let result0 = calculate(0)
        async let result1 = calculate(1)  
        async let result2 = calculate(2)
        print("Tasks started concurrently")
        // 等待所有任务完成
        let (r0, r1, r2) = try await (result0, result1, result2)
        print("Program ended")

    }

从代码中可以看到,aysnc let 语法比较简洁,类似普通声明变量,只不过这里是一个异步函数,声明时立即开始并发执行,可以处理不同类型的返回值,需要明确使用try await 等待所有结果

3.两者对比:

  1. group可以动态的表达任务的数量 通过一个for循环来创建所有的任务,但是他要求所有返回值都是同一个类型
  2. asyn let提高了灵活性,但是对于动态任务组表达比较繁琐,比较适合处理固定数量的异步任务
相关推荐
大熊猫侯佩15 小时前
SwiftUI 6.0(iOS 18)自定义容器值(Container Values)让容器布局渐入佳境(下)
swiftui·swift·apple
大熊猫侯佩17 小时前
用接地气的例子趣谈 WWDC 24 全新的 Swift Testing 入门(二)
单元测试·swift·apple
我现在不喜欢coding17 小时前
swift中的self,Self,Class(struct).Type让你头大了嘛?
ios·swift
不二狗17 小时前
每日算法 -【Swift 算法】查找字符串数组中的最长公共前缀
开发语言·算法·swift
不二狗17 小时前
每日算法 -【Swift 算法】将整数转换为罗马数字
开发语言·算法·swift
InternLM1 天前
论文分类打榜赛Baseline:ms-swift微调InternLM实践
swift·internlm·书生
YungFan2 天前
SwiftUI-Markdown渲染
swiftui·swift
我现在不喜欢coding2 天前
swift为什么会需要mutating关键字
swift