我在Swift 5.5中遇到了一个问题,我不太明白解决方案。
import Foundation
func testAsync() async {
var animal = "Dog"
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
animal = "Cat"
print(animal)
}
print(animal)
}
Task {
await testAsync()
}这段代码会导致错误。
Mutation of captured var 'animal' in concurrently-executing code但是,如果您将animal变量从这个异步函数的上下文中移开,
import Foundation
var animal = "Dog"
func testAsync() async {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
animal = "Cat"
print(animal)
}
print(animal)
}
Task {
await testAsync()
}它将汇编。我理解这个错误是为了防止数据竞争,但是为什么移动变量会使其安全?
发布于 2022-11-10 17:53:57
关于globals示例的行为,我可能会向您介绍与globals的可发送性相关的罗伯·纳皮尔的评论 re bug/限制:
编译器在如何推理全局变量方面有许多限制。简短的回答是“不要让全局变量变。”它出现在论坛上,但没有得到任何讨论。https://forums.swift.org/t/sendability-checking-for-global-variables/56515
FWIW,如果将其放在实际的应用程序中,并将“严格并发检查”构建设置更改为“完成”,则在全局示例中确实会收到适当的警告:
对var的引用不是并发安全的,因为它涉及共享的可变状态。
这种对线程安全问题的编译时检测正在发展,Swift 6中出现了许多新的错误(这就是为什么他们给了我们这个新的“严格并发检查”设置,这样我们就可以开始使用不同级别的检查来检查代码了)。
无论如何,您可以使用一个参与者提供线程安全的交互,其值如下:
actor AnimalActor {
var animal = "Dog"
func setAnimal(newAnimal: String) {
animal = newAnimal
}
}
func testAsync() async {
let animalActor = AnimalActor()
Task {
try await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC)
await animalActor.setAnimal(newAnimal: "Cat")
print(await animalActor.animal)
}
print(await animalActor.animal)
}
Task {
await testAsync()
}有关更多信息,请参见WWDC 2021的用斯威夫特演员保护易变状态和2022年的使用Swift并发消除数据竞赛。
注意,在上面,我避免使用GCD。asyncAfter是一种古老的GCD技术,用于在不阻塞当前线程的同时推迟某些工作。但是新的Task.sleep (与旧的Thread.sleep不同)在并发系统中实现了相同的行为(并提供取消功能)。在可能的情况下,我们应该避免Swift并发代码库中的GCD。
https://stackoverflow.com/questions/74372835
复制相似问题