主线程上运行的代码是否保证对“MainActor.assumeIsolated”安全

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

我有以下代码,我不确定它是否安全:

extension Timer {

  @MainActor // <- 1
  static func myScheduled(
    interval: TimeInterval, 
    block: @escaping @MainActor @Sendable () -> Void) -> Timer // <- 2
  {
    
    return scheduledTimer(withTimeInterval: interval, repeats: true) { _ in
      MainActor.assumeIsolated(block) // <- 3
    }
  }
}

在上面的代码中,第一个

@MainActor
注释(代码中标记1)保证必须从主参与者隔离上下文中调用
myScheduled
函数,这意味着
Timer.scheduledTimer
将把计时器放在
RunLoop.main
上,这意味着必须在主线程上调用计时器回调。

我的

block
需要在主要演员上调用一些API,所以我必须将其标记为主要演员(代码中标记2)。但是,由于编译器不知道计时器回调将在主线程上运行,因此我必须在这里使用
MainActor.assumeIsolated(block)
(代码中的标记 3)来消除编译器警告。

我的问题是,做出这个假设总是安全的吗?我确信计时器回调发生在主线程上,但是,我不确定假设主线程上下文必须是主隔离的是否总是安全的。

来自

MainActor.assumeIsolated
的API文档:

此方法允许假设并验证当前正在执行的同步函数实际上是在 MainActor 的串行执行器上执行。

这个检查是针对MainActor的串行执行器执行的,这意味着/如果另一个actor使用相同的串行执行器——通过使用sharedUnownedExecutor作为自己的unownedExecutor——这个检查将会成功,因为从并发安全的角度来看,串行执行器保证了互斥那两个演员。

换句话说,如果某个东西在主线程上运行,我们确定它一定是由这个主要参与者的“执行者”执行吗?

ios swift concurrency
1个回答
0
投票

主要参与者的执行者是主调度队列,它是与主线程关联的调度队列,所以是的,你的代码是安全的。

来自

MainActor

一个单例参与者,其执行者相当于主调度队列。

但是,正如

assumeIsolated
的文档所述,这只能作为最后的手段。在这种情况下,更好的方法是使用
Timer
来消耗
Task
。现在
block
甚至不需要是
@Sendable

@MainActor
static func myScheduled(
    interval: TimeInterval,
    block: @escaping @MainActor () -> Void) -> Task<Void, Never>
{
    Task {
        for await _ in publish(every: interval, on: .main, in: .default).values {
            block()
        }
    }
}

Task.init
创建一个在当前参与者(即主要参与者)上运行的任务。

通过返回

Task
,调用者仍然可以通过调用
invalidate
Task.cancel
计时器。

© www.soinside.com 2019 - 2024. All rights reserved.