You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用DispatchSerialQueue作为Actor自定义Executor时assumeIsolated调用失败

问题:自定义Dispatch队列作为Actor Executor时,assumeIsolated触发运行时错误

我想实现一个Actor,用来适配NWNetwork这类需要指定Dispatch队列和回调的“遗留”框架。为了避免在回调里通过Task调度到隔离上下文,我让整个Actor运行在自定义的DispatchSerialQueue上,这个队列同时也是遗留框架使用的队列。

我把回调写成了@Sendable且nonisolated的成员函数,确保回调会在指定队列上调用,但调用self.assumeIsolated(_:)时触发了运行时错误:

Fatal error: Incorrect actor executor assumption; Expected same executor as MyLib.TestActor.

最小测试用例里,await actor.isolatedFunc()能正常运行,但模拟遗留框架调用回调时就报错,堆栈跟踪显示当前确实在指定队列上。我认为DispatchSerialQueue应该是合规的SerialExecutor,断言应该通过,求问题原因和解决办法。


测试Actor代码

public final actor TestActor {
    let queue = DispatchSerialQueue(label: "test_actor_queue")
 
    nonisolated
    public var unownedExecutor: UnownedSerialExecutor {
        queue.asUnownedSerialExecutor()
    }
    
    func isolatedFunc() {
        self.preconditionIsolated()
    }
    
    @Sendable
    nonisolated
    func nonIsolatedFunc() {
        self.queue.async {
            self.callback()
        }
    }
    
    @Sendable
    nonisolated
    func callback() {
        dispatchPrecondition(condition: .onQueue(queue))

        self.assumeIsolated { this in    // <== 运行时错误
            this.isolatedFunc()
        }
    }
}

XCTest测试代码

final class HTTPServerTests: XCTestCase {
    
    func testExample() async throws {
        let actor = TestActor()
        
        await actor.isolatedFunc()
        actor.nonIsolatedFunc()
        
    }
}    

堆栈跟踪

Thread 4 Queue : test_actor_queue (serial)
#0  0x000000019326df68 in _swift_runtime_on_report ()
#1  0x000000019330614c in _swift_stdlib_reportFatalErrorInFile ()
#2  0x0000000192fcb2d8 in _assertionFailure(_:_:file:line:flags:) ()
#3  0x00000002099cb298 in Actor.assumeIsolated<τ_0_0>(_:file:line:) ()
#4  0x00000001053a2918 in TestActor.callback@Sendable ()
#5  0x00000001053a27a0 in closure #1 in TestActor.nonIsolatedFunc@Sendable ()
#6  0x00000001053a2978 in thunk for @escaping @callee_guaranteed @Sendable () -> () ()

问题原因与解决办法

原因分析

Swift Actor的assumeIsolated不仅会检查当前是否在指定的串行队列上,还会验证当前执行上下文的Executor身份是否和Actor的unownedExecutor完全匹配。

这里的核心问题是:DispatchSerialQueue.asUnownedSerialExecutor()每次调用都会返回一个新的UnownedSerialExecutor实例,而Actor的unownedExecutor属性每次访问也会创建新实例。当你在回调里调用assumeIsolated时,系统会拿当前队列的Executor实例和Actor的unownedExecutor返回的实例做身份比较——因为是两个不同的实例,所以断言失败。

解决办法

把queue.asUnownedSerialExecutor()的结果缓存起来,让Actor的unownedExecutor始终返回同一个实例:

修改后的TestActor代码:

public final actor TestActor {
    let queue = DispatchSerialQueue(label: "test_actor_queue")
    // 缓存Executor实例,确保身份唯一
    private let executor: UnownedSerialExecutor
 
    nonisolated
    public var unownedExecutor: UnownedSerialExecutor {
        executor
    }
    
    init() {
        executor = queue.asUnownedSerialExecutor()
    }
    
    func isolatedFunc() {
        self.preconditionIsolated()
    }
    
    @Sendable
    nonisolated
    func nonIsolatedFunc() {
        self.queue.async {
            self.callback()
        }
    }
    
    @Sendable
    nonisolated
    func callback() {
        dispatchPrecondition(condition: .onQueue(queue))

        self.assumeIsolated { this in
            this.isolatedFunc()
        }
    }
}

补充说明

  • 当你通过await actor.isolatedFunc()调用时,Swift会自动使用Actor的unownedExecutor来调度任务,此时Executor实例是一致的,所以不会报错。
  • 缓存Executor实例后,无论多少次访问unownedExecutor,返回的都是同一个对象,和当前队列的Executor实例身份完全匹配,assumeIsolated的断言就会通过。

内容的提问来源于stack exchange,提问作者CouchDeveloper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 02:18:22