macOS 14(Sonoma)下SwiftUI任务无法并发运行,如何正确实现?
Sonoma系统下SwiftUI任务并发异常问题解决
在Sonoma系统中使用SwiftUI时,两个Task无法异步并发执行,系统升级前可正常工作。代码如下:
import SwiftUI struct ContentView: View { var body: some View { Button("Press me") { Task { for i in (1...5) { print ("a", i) sleep(1) } } Task { for i in (1...5) { print ("b", i) sleep(1) } } } } }
运行输出为顺序执行的结果:
a 1 a 2 a 3 a 4 a 5 b 1 b 2 b 3 b 4 b 5
或偶尔:
b 1 b 2 b 3 b 4 b 5 a 1 a 2 a 3 a 4 a 5
但在Playground中运行时,两个任务输出会交错显示,符合并发预期。
问题原因
- sleep的阻塞特性:
sleep(1)是阻塞式函数,会占用当前线程直到休眠结束,无法让调度器切换到其他任务。 - Actor上下文继承:SwiftUI的Button动作闭包默认运行在
MainActor上,直接创建的Task会继承这个串行Actor上下文,导致两个任务只能按顺序执行,无法并发。而Playground的执行环境没有强制绑定到串行Actor,所以能表现出并发效果。
正确实现并发的方法
方法1:使用Task.detached脱离当前Actor上下文
通过Task.detached创建不继承当前Actor的任务,让任务在全局并发队列执行:
Button("Press me") { Task.detached { for i in (1...5) { print ("a", i) sleep(1) } } Task.detached { for i in (1...5) { print ("b", i) sleep(1) } } }
方法2:使用异步休眠替代阻塞式sleep
用try await Task.sleep(nanoseconds:)替代sleep,它是异步非阻塞的,能让系统调度器合理分配线程,即使在MainActor上下文也能实现任务切换(注意需要处理抛出的错误):
Button("Press me") { Task { for i in (1...5) { print ("a", i) try await Task.sleep(nanoseconds: 1_000_000_000) } } Task { for i in (1...5) { print ("b", i) try await Task.sleep(nanoseconds: 1_000_000_000) } } }
如果不需要处理错误,也可以用try? await忽略错误:
try? await Task.sleep(nanoseconds: 1_000_000_000)
两种方法都能让任务实现并发执行,输出会交错显示。
内容的提问来源于stack exchange,提问作者iphaaw
相关产品推荐
相关产品推荐

