Swift非actor隔离闭包

3

当传递给actor方法的逃逸闭包继承actor隔离吗?还是它们是非隔离的?

例如:

actor MyActor {
   func method(_ closure: @escaping () async -> Void) {
      await closure()
   }
}

闭包将使用哪种隔离方式进行创建? 在我的简单测试中,似乎闭包在分配时继承了它的上下文隔离。

actor MyActor {
   func method(_ closure: @escaping () async -> Void) async {
       print("in actor method: ", Thread.current)
       await closure()
       print("in actor method: ", Thread.current)
   }
}

func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        let actor = MyActor()
        Task {
            print("in task closure: ", Thread.current)
            await actor.method {
                print("in actor closure: ", Thread.current)
                try? await Task.sleep(nanoseconds: 1_000_000_000)
                print("in actor closure: ", Thread.current)
            }
            print("in task closure: ", Thread.current)
        }
        
        return true
}

输出:

in task closure:  <_NSMainThread: 0x283604380>{number = 1, name = main}
in actor method:  <NSThread: 0x283654400>{number = 5, name = (null)}
in actor closure:  <_NSMainThread: 0x283604380>{number = 1, name = main}
in actor closure:  <_NSMainThread: 0x283604380>{number = 1, name = main}
in actor method:  <NSThread: 0x283654400>{number = 5, name = (null)}
in task closure:  <_NSMainThread: 0x283604380>{number = 1, name = main}

我知道这并不是做出假设的正确证明,因此我想问:有没有任何建议或声明可以描述异步闭包所获得的隔离性?

1个回答

3
是的,闭包将使用形成它的相同演员上下文(在此示例中为主要演员):
正如SE-0306所说

一个不是@Sendable的闭包不能逃脱它形成的并发域。因此,如果该闭包在演员隔离的上下文中形成,则它将被演员隔离。


如果您不想让闭包在其形成的演员上运行,您有几个选项:
  1. You can make the closure @Sendable. As SE-0306 says:

    Actors [specify] that a @Sendable closure (described in Sendable and @Sendable closures, and used in the definition of detach in the Structured Concurrency proposal) is always non-isolated.

    Thus,

    actor MyActor {
        func method(_ closure: @escaping @Sendable () async -> Void) async {
            ...
        }
    }
    

    That results in the closure being non-isolated (i.e., not running on the actor where the closure was created).

  2. You can also explicitly specify the closure’s actor. You could specify a global actor:

    @globalActor
    public struct SomeGlobalActor {
        public actor SomeOtherActor { }
    
        public static let shared = SomeOtherActor()
    }
    
    actor MyActor {
        func method(_ closure: @escaping @SomeGlobalActor () async -> Void) async {
            ...
        }
    }
    

谢谢,很有趣。需要深入了解@Sendable。我以为它只是对编译步骤添加了一些限制,没有任何隔离变化。 - Pyrettt Pyrettt
2
@Sendable 使闭包非隔离(意味着它不必在调用者的 actor 上运行)。已更新上面的内容,其中包含来自 SE-0306 的相关摘录。 - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接