Swift Actor是否仅支持并发而非并行执行?
Swift Actor 并行/并发执行机制答疑
核心结论:Actor 既支持并发隔离,也支持真正的多核并行,你测试中观察到的串行执行现象,和「同类型Actor共享执行器」没有任何关系,问题出在测试方法和任务调度规则的认知偏差上。
初始TaskGroup测试串行的根本原因
你第一版的测试代码如下:
actor SomeActor { func check() { print("before sleep") sleep(5) print("after sleep") } } let act1 = SomeActor() let act2 = SomeActor() Task { await withTaskGroup(of: Void.self) { group in group.addTask { await act1.check() } group.addTask { await act2.check() } } }
这段代码输出串行,源于两个核心问题:
- 你用了C标准库的
sleep(5),这是阻塞式系统调用,会直接占住当前操作系统线程,完全不给Swift并发运行时让出执行权的机会。Swift并发是协作式调度,只有任务执行到await挂起点时才会主动让出线程供其他任务使用,阻塞调用直接卡死线程,调度器根本没法在这个线程上安排其他任务。 - 外层
Task{}没有指定独立执行器,group.addTask默认继承当前上下文的隔离域与执行器优先级,如果当前运行上下文是串行的(比如主线程、或全局并发池在当前负载下只为同优先级任务分配了单线程),第一个任务占住线程sleep,第二个任务拿不到可用线程,自然只能等第一个执行完。
这里明确澄清:不存在「同一个Actor类型的所有实例共享同一个执行器」的隐式规则。每个自定义Actor实例默认持有独立的串行隔离域,不同实例之间没有互斥限制,天生支持跨核并行。
后续两组对照测试的现象解释
1. detached任务+MainActor任务实现并行
你的测试代码:
let act1 = SomeActor() let act2 = SomeActor() Task.detached { await act1.check() } Task { @MainActor in await act2.check() }
这段代码输出并行,逻辑非常直接:
Task.detached不继承当前上下文的隔离域,默认将任务派发到全局后台并发线程池执行- 标注
@MainActor的任务会单独调度到主线程这个独立执行器上运行
两个任务分别跑在两个完全独立的系统线程上,哪怕用sleep阻塞,两个线程互不干扰,自然呈现并行效果。
2. 两个detached任务回到串行
你的测试代码:
let act1 = SomeActor() let act2 = SomeActor() Task.detached { await act1.check() } Task.detached { @MainActor in await act2.check() }
这里出现串行,本质还是阻塞调用导致的调度异常:第一个detached任务占住后台线程sleep时,如果当前全局并发池没有空闲线程处理MainActor的调度请求、或者测试运行在没有启动RunLoop的命令行环境下MainActor本身无法正常调度任务,就会出现等第一个任务跑完才轮到第二个的现象,和Actor本身的并行能力无关。
正确的Actor并行性验证方法
要验证Actor的并行能力,绝对不能用阻塞线程的同步API,必须换成Swift并发提供的异步挂起API,让调度器可以正常工作,测试代码如下:
actor SomeActor { func check() async { print("before sleep - \(ObjectIdentifier(self))") // 异步等待5秒,不会阻塞当前线程,主动让出执行权 try? await Task.sleep(nanoseconds: 5_000_000_000) print("after sleep - \(ObjectIdentifier(self))") } } let act1 = SomeActor() let act2 = SomeActor() Task { await withTaskGroup(of: Void.self) { group in group.addTask { await act1.check() } group.addTask { await act2.check() } } }
运行这段代码你会看到两个before sleep几乎同时打印,等待5秒后两个after sleep也几乎同时输出,直接证明两个不同Actor实例的方法可以完全并行执行。
关键规则整理
- 单个Actor实例的核心特性是串行隔离:同一时间只会有一个任务执行该实例的隔离代码,从根源上避免数据竞争,这是Actor并发模型的核心设计目标
- 不同Actor实例之间没有共享隔离域,只要系统有可用CPU核心,调度器会自动将不同实例上的任务分配到不同线程并行执行,没有同类型实例互斥的限制
- 永远不要在Swift并发上下文中使用阻塞线程的同步API(包括同步sleep、长时间持有的同步锁、阻塞式IO等),这类API会破坏协作式调度逻辑,导致任务调度异常,产生不符合预期的串行现象
- 任务最终是否能并行,除了Actor隔离规则之外,还受任务继承的执行器、全局并发池的线程分配策略、当前系统CPU负载影响,这些是调度层的逻辑,和Actor本身的并行支持能力无关
内容的提问来源于stack exchange,提问作者Pyrettt Pyrettt
相关产品推荐
相关产品推荐

