子类继承了@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
继承自UIResponder
,UIResponder
是使用@MainActor
声明的。从我对这个错误的理解来看,Sendable闭包是一个捕获所有可发送值的闭包,即可以安全地从多个线程访问。但是在这种情况下,我不关心多个线程同时更改这个变量的值。为了在我的测试中解决这个问题,我不得不这么做:
group.addTask { @MainActor in
await self.uploadImage(of: name)
self.lastUploaded = name
}
但这是我对这样做的担忧:如果@MainActor
闭包在主线程上运行,那不会因为上传而冻结UI吗?除此之外,它还违背了使用TaskGroup的目的,因为所有这些上传都会在主线程上排队,这基本上会使它们由于“await”关键字而顺序发生。
所以我的问题是,如何异步修改继承@MainActor
属性的属性?有没有办法关闭这种独占/同步访问?
**编辑:**我只想知道是否可以删除属性上继承的@MainActor
声明。也许可以将其声明为nonisolated
。
1条答案
按热度按时间omqzjyyz1#
你问:
如果@MainActor闭包在主线程上运行,那不会因为上传而冻结UI吗?
不,当你到达
await
时,主要参与者不会被阻止,而是任务暂停,主要参与者可以在上传过程中自由切换到其他任务。await
不会阻止主要参与者。(这与传统的阻塞,wait
API相反。)你接着问:
有没有办法关闭这种独占/同步访问?
你正在编写多线程代码,Swift并发有这些机制来确保你的代码是线程安全的。我强烈建议你接受这些模式(当你第一次遇到这些新警告时,它们可能会让你感到困惑)。
但是,在回答你的问题时,理论上你可以放弃Swift并发性,编写使用遗留API的代码,而不需要编译时检查线程安全性。但是我建议不要这样做。一旦你掌握了Swift并发性,actor等,你就会开始真正欣赏它,再也不想回到脆弱的遗留模式。
顺便说一下,考虑到你对Swift并发和线程的各种问题,你可能会发现WWDC 2021视频Swift concurrency: Behind the scenes很有趣。它涵盖了很多关于Swift并发的有用背景(包括它的线程模型)。它也涵盖了很多其他材料,但它可能有助于你熟悉
await
“挂起点”,“延续”等概念。