swift 如何在继承@MainActor时异步修改MainActor隔离属性

h79rfbju  于 2023-03-22  发布在  Swift
关注(0)|答案(1)|浏览(239)

子类继承了@MainActor属性,该属性确保主线程同步访问其所有存储的内容。但是如果我不想要这种“独占访问”功能呢?以下面的例子为例:

class GameScene: SKScene {
    var imagesToUpload = ["cat", "mouse", "dog"]
    var lastUploaded = ""
    
    func uploadAllImages() async {
        await withTaskGroup(of: Void.self) { group in
            for name in imagesToUpload {
                group.addTask {
                    await self.uploadImage(of: name)
                    self.lastUploaded = name  // << Error: Main actor-isolated property 'lastUploaded' can not be mutated from a Sendable closure
                } 
            }
        }
    }
    
    func uploadImage(of: String) async {
        try! await Task.sleep(for: .seconds(1))
    }
}

SKScene继承自UIResponderUIResponder是使用@MainActor声明的。从我对这个错误的理解来看,Sendable闭包是一个捕获所有可发送值的闭包,即可以安全地从多个线程访问。但是在这种情况下,我不关心多个线程同时更改这个变量的值。为了在我的测试中解决这个问题,我不得不这么做:

group.addTask { @MainActor in
    await self.uploadImage(of: name)
    self.lastUploaded = name
}

但这是我对这样做的担忧:如果@MainActor闭包在主线程上运行,那不会因为上传而冻结UI吗?除此之外,它还违背了使用TaskGroup的目的,因为所有这些上传都会在主线程上排队,这基本上会使它们由于“await”关键字而顺序发生。
所以我的问题是,如何异步修改继承@MainActor属性的属性?有没有办法关闭这种独占/同步访问?

**编辑:**我只想知道是否可以删除属性上继承的@MainActor声明。也许可以将其声明为nonisolated

omqzjyyz

omqzjyyz1#

你问:
如果@MainActor闭包在主线程上运行,那不会因为上传而冻结UI吗?
不,当你到达await时,主要参与者不会被阻止,而是任务暂停,主要参与者可以在上传过程中自由切换到其他任务。await不会阻止主要参与者。(这与传统的阻塞,wait API相反。)
你接着问:
有没有办法关闭这种独占/同步访问?
你正在编写多线程代码,Swift并发有这些机制来确保你的代码是线程安全的。我强烈建议你接受这些模式(当你第一次遇到这些新警告时,它们可能会让你感到困惑)。
但是,在回答你的问题时,理论上你可以放弃Swift并发性,编写使用遗留API的代码,而不需要编译时检查线程安全性。但是我建议不要这样做。一旦你掌握了Swift并发性,actor等,你就会开始真正欣赏它,再也不想回到脆弱的遗留模式。
顺便说一下,考虑到你对Swift并发和线程的各种问题,你可能会发现WWDC 2021视频Swift concurrency: Behind the scenes很有趣。它涵盖了很多关于Swift并发的有用背景(包括它的线程模型)。它也涵盖了很多其他材料,但它可能有助于你熟悉await“挂起点”,“延续”等概念。

相关问题