如何使用Swift新结构化并发启动后台任务?
适用场景说明
你提到的无async标记的同步阻塞代码放到后台执行的场景,苹果官方有以下几种推荐的实现方式,优先级高于Task.detached:
1. 异步测试上下文下的带优先级Task
Xcode 13及以上版本的XCTest原生支持async测试方法,你可以直接将测试函数标记为async,通过带优先级配置的普通Task执行同步代码:
func testBlockingFunction() async throws { // 指定后台优先级执行同步阻塞代码 let result = await Task(priority: .background) { // 此处调用无async标记的待测阻塞函数 return yourBlockingFunction() }.value // 后续执行断言逻辑 }
普通Task会继承当前测试任务的上下文,包括取消状态、任务优先级等,完全符合结构化并发的约束,测试触发取消时会自动终止子任务,不会出现任务泄漏,是官方首推的轻量方案。
2. GCD桥接方案(适用于长时间阻塞的代码)
结构化并发的底层线程池是协作式调度的,不适合执行长时间阻塞的任务,否则会占用线程资源影响其他任务调度。这种场景下官方推荐使用withCheckedThrowingContinuation桥接GCD队列实现:
func testLongBlockingFunction() async throws { let result = try await withCheckedThrowingContinuation { continuation in // 显式指定将阻塞代码放到全局后台队列执行 DispatchQueue.global(qos: .background).async { do { let res = try yourThrowingBlockingFunction() continuation.resume(returning: res) } catch { continuation.resume(throwing: error) } } } // 后续执行断言逻辑 }
这种方案可以完全控制阻塞代码的执行队列,不会占用结构化并发的协作线程池,稳定性更高。
3. 多任务场景下配合async let使用
如果需要同时执行多个阻塞任务,可以把桥接逻辑封装为独立的异步函数,之后通过async let声明并行执行:
// 封装同步阻塞函数为异步接口 func wrappedBlockingFunc() async throws -> ResultType { try await withCheckedThrowingContinuation { continuation in DispatchQueue.global().async { continuation.resume(returning: yourBlockingFunction()) } } } func testMultiBlockingTasks() async throws { // 并行启动多个阻塞任务 async let result1 = wrappedBlockingFunc() async let result2 = wrappedBlockingFunc() // 等待所有任务执行完成 let allResults = try await [result1, result2] // 批量执行断言 }
补充说明
官方不推荐使用Task.detached的核心原因是分离任务完全脱离当前任务树,不会继承上下文的取消状态、优先级、任务本地存储等属性,容易出现测试已经结束但后台任务仍在执行的泄漏问题,影响测试稳定性。
内容的提问来源于stack exchange,提问作者joels
相关产品推荐
相关产品推荐

