我在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()
}

它会编译的.我理解这个错误是为了防止数据竞争,但为什么移动变量会使它变得安全?

推荐答案

关于全局变量示例的行为,我可能会向您推荐与全局变量可发送性相关的Rob Napier’s comment个Re错误/限制:

编译器在如何对全局变量进行推理方面有许多限制.简短的答案是"不要让全局变量成为可变变量."它出现在论坛上,但还没有得到任何讨论.https://forums.swift.org/t/sendability-checking-for-global-variables/56515

FWIW,如果您将此代码放入实际的应用程序中,并将"严格并发判断"构建设置更改为"完成",则会收到全局示例中的相应警告:

对var‘Animate’的引用不是并发安全的,因为它涉及共享可变状态

这种对线程安全问题的编译时检测正在发展中,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的Eliminate data races using Swift Concurrency33" rel="nofollow noreferrer">Protect mutable state with Swift actors和2022的Eliminate data races using Swift Concurrency.


注意,在上面的内容中,我避免使用GCD API.asyncAfter是一种古老的GCD技术,用于在不阻塞当前线程的情况下推迟一些工作.但新的Task.sleep(与旧的Thread.sleep不同)在并发系统中实现了相同的行为(并提供了取消功能).在可能的情况下,我们应该避免在SWIFT并发代码库中使用GCD API.

Swift相关问答推荐

SwiftUI正在初始化不带状态变量的绑定变量

在visionOS 1.0中已弃用';base Color';:请改用`Color`属性

是否可以将具有关联值的枚举强制转换为其类型与关联值匹配的变量?

如何在 Vapor 中制作可选的查询过滤器

使用变量(而非固定)的字符串分量进行Swift正则表达式

Xcode创建GitHub仓库,但未推送所有文件

在这个使用 URLSession 的简单 case 中是否创建了一个强引用循环?

Swift Combine:如何在保留发布者结果顺序的同时进行收集?

动画偏移正在 destruct 按钮 SwiftUI

将 struct 作为泛型类型传递并访问该泛型类型属性

如何将回调传递给 View init

在 Swift 中实现自定义异步序列

否定 #available 语句

playground 导入:没有这样的模块Foo

Swift 中的 CommonHMAC

Alamofire:如何全局处理错误

URLComponents.url 为零

在 Swift 中覆盖 UIScrollView 的委托属性(就像 UICollectionView 一样)

WKWebView 确实从本地文档文件夹加载资源

判断用户是否登录到 iCloud?Swift /iOS