Actor 符合具有异步函数要求的协议

问题描述 投票:0回答:1

我有一个具有同步功能要求的协议

class NonSendable {}

protocol P1 {
  func doSomething(_ nonSendable: NonSendable) async
}

当我使用

actor
遵守此协议时,同时启用快速并发检查,我会收到警告

actor P1Actor: P1 {
 // Warning: Non-sendable type 'NonSendable?' returned by actor-isolated instance method 'doSomething' satisfying protocol requirement cannot cross actor boundary
 func doSomething(_ nonSendable: NonSendable) {}
}

但是当我将

actor
更改为
struct
class
时,警告就会消失。

struct P1Struct: P1 {
 // No warning
 func doSomething(_ nonSendable: NonSendable) async {}
}

我不明白为什么我会收到演员的警告。我很困惑,因为在所有情况下

doSomething
都会从异步上下文触发,那么当函数隔离到
actor
时,为什么参数需要是可发送的?

swift concurrency swift6
1个回答
0
投票

回想一下,非

Sendable
类型的值不能跨越参与者边界。

当您从非隔离上下文调用

doSomething
时,这正是传递给
doSomething
的参数将要做的事情 - 被发送到
P1Actor
。您肯定会同意这违反了
Sendable
的规则:

class Foo {
    let x = NonSendable()
    func foo() async {
        let p1 = P1Actor()
        // x is not Sendable but its being sent to p1!
        await p1.doSomething(x) // error here!
    }
}

如果

doSomething
不满足任何协议要求(例如,如果
P1Actor
不符合
P1
),Swift 仍然允许您声明此方法,因为它可以在调用站点发出错误(如上面示例)。

但是,如果

doSomething
是协议要求,则 Swift 无法再在调用站点判断对
doSomething
的调用是否会将不可发送的值发送给另一个参与者。考虑:

class Foo {
    let x = NonSendable()
    func foo(p: any P1) async {
        // is x being sent across actor boundaries? It depends on whether p is an actor!
        await p.doSomething(x)
    }
}

在编译时,不知道

p
是否是一个 actor,因此 Swift 必须首先禁止你声明
doSomething


解决此问题的一种方法是让协议见证

nonisolated
。让它从不可发送的参数值中提取可发送的内容,然后调用实际的隔离实现。举个例子:

actor P1Actor: P1 {
    nonisolated func doSomething(_ nonSendable: NonSendable) async {
        someIsolatedImplementation(nonSendable.someString, nonSendable.someNumber, nonSendable.someFlag)
    }
    
    // the parameters of this are all sendable!
    func someIsolatedImplementation(_ a: String, _ b: Int, _ c: Bool) {
        
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.