Swift新Concurrency框架下实现「单次执行、等待全部完成」并发模式
问题分析
你要实现的是**「合并重复请求」**的并发模式:当异步任务正在执行时,后续请求不会重复触发任务,而是等待当前任务完成后统一获取结果。原DispatchQueue版本通过串行队列保护状态、存储回调数组,任务完成后批量执行回调;而你尝试的Actor版本,后续请求看到isRunning为true时直接返回,未等待任务完成,导致提前结束,不符合预期。
纯Swift Concurrency实现方案
我们可以在Actor中维护当前正在执行的任务引用,让后续请求直接await该任务,利用Swift异步机制自动处理结果传递,无需手动管理回调数组:
actor NewTest { private var currentTask: Task<Void, Error>? func callMe(id: Int) async throws { print("\(id) called") if let existingTask = currentTask { // 已有任务在运行,等待任务完成 try await existingTask.value } else { // 无任务运行,创建新任务执行异步工作 print("\(id) is going to start doing something useful") let task = Task { try await self.doSomeAsyncStuff() } currentTask = task do { try await task.value } catch { // 传递任务执行中的错误 throw error } finally { // 任务完成(成功/失败)后清空任务引用 currentTask = nil } } } private func doSomeAsyncStuff() async throws { try await Task.sleep(nanoseconds: 1_000_000_000) print("finished doing something useful") } }
调用代码
let test = NewTest() // 用TaskGroup替代concurrentPerform,适配Swift Concurrency异步模型 Task { await withTaskGroup(of: Void.self) { group in for i in 0..<3 { group.addTask { try await test.callMe(id: i) print("\(i) is done") } } } }
核心逻辑输出(调度顺序可能略有差异,但核心行为一致)
0 called 0 is going to start doing something useful 1 called 2 called finished doing something useful 0 is done 1 is done 2 is done
方案说明
- 任务引用管理:通过
currentTask存储当前运行的异步任务,确保同一时间仅一个任务执行。 - 自动等待机制:后续请求发现已有任务时,直接
await任务结果,无需手动存储回调,Swift异步语法自动完成结果传递。 - 错误一致性:任务执行中的错误会通过
try await传递给所有等待的请求,保证所有请求拿到相同的错误信息。 - 资源清理:在
finally块中清空任务引用,确保任务完成后Actor可以接受新的请求触发任务。
该方案完全基于Swift Concurrency框架,无需依赖GCD,完美复刻原模式的核心行为:合并重复请求、避免任务重复执行、所有请求等待任务完成后统一返回结果。
内容的提问来源于stack exchange,提问作者timbre timbre
相关产品推荐
相关产品推荐

