为何使用全局Actor保障串行执行,读写操作却并行运行?
问题原因分析
你的代码里读写操作并行执行的核心原因是:Task.sleep会挂起当前Actor任务并释放Actor的执行权,导致其他等待的Actor任务被提前调度。
详细执行流程
- 测试中
task1提交write任务到DataStorageActor的队列,Actor开始执行write方法,打印Write started。 - 当
write方法执行到try await Task.sleep(5_000_000_000)时,这个await会让当前Actor任务挂起——因为Task.sleep是在全局执行上下文运行的异步操作,不依赖当前Actor。此时Actor会释放执行权,去处理队列里的下一个等待任务(也就是task2提交的read操作)。 read方法在Actor上执行完成,读取到还未被赋值的nil,打印相关日志后返回。- 5秒后
Task.sleep完成,Actor回到挂起的write任务,执行storage[key] = value并打印Write completed。 - 此时
task1才完成,测试继续等待task2,最终断言失败。
为什么全局Actor没起到预期的串行效果?
全局Actor保证的是Actor任务队列的串行执行,但它的串行性是针对「Actor任务的执行片段」,而非整个异步方法的全程。当方法内部await一个脱离Actor上下文的异步操作时,Actor会暂时让出执行权,优先处理队列中的其他任务。
修复方案
如果你需要让整个写入操作(包括模拟的耗时过程)都独占Actor,不让其他操作插队,可以采用以下方式:
用同步耗时操作替代Task.sleep
将Task.sleep换成同步的阻塞延迟(仅用于测试场景,生产环境不推荐阻塞Actor线程):
func write(key: String, value: String) async throws { print("\(Date()): Write started") // 同步阻塞5秒,不会释放Actor执行权 Thread.sleep(forTimeInterval: 5) storage[key] = value print("\(Date()): Write completed for key: \(key)") }
修改后,write方法会全程占用Actor执行权,read操作必须等write完全执行完毕才能开始,符合你预期的串行效果。
内容的提问来源于stack exchange,提问作者user2828120
相关产品推荐
相关产品推荐

