在并行执行的代码中引用捕获的变量

huangapple go评论76阅读模式
英文:

Reference to captured var in concurrently-executing code

问题

抱歉,代码部分我无法提供翻译。

英文:

I was trying out the new async/await pattern in swift and I came accross something which I found it confusing.

struct ContentView: View {
    var body: some View  {
        Text("Hello World!")
            .task {
                var num = 1

                Task {
                    print(num)
                }

                Task {
                    print(num)
                }
            }
    }

    func printScore() async {
        var score = 1

        Task { print(score) }
        Task { print(score) }
    }
}

在并行执行的代码中引用捕获的变量

Can someone please clarify looking at the above screenshot on why the compiler only complaints about captured var inside printScore() function and does not complaint when the same is being done using the task modifier on the body computed property of the ContentView struct (i.e Line 14-24) ?

This is the example I came up with and got confused on the compiler behavior.I also change the compiler setting "Strict Concurrency Checking” build setting to “Complete” and still don't see the compiler complaining.

答案1

得分: 7

这是@MainActor的一项特殊功能。在一个View中,body 被标记为 MainActor:

@ViewBuilder @MainActor var body: Self.Body { get }

Tasks继承了它们调用者的上下文,因此它们也是MainActor。如果你在body中将Task替换为Task.detached,你将看到相同的错误,因为这将使Task移出MainActor的上下文。

相反,如果你给printScore添加@MainActor,它也会编译通过而没有错误:

@MainActor func printScore() async {
    var score = 1

    Task { print(score) }
    Task { print(score) }
}

score继承了MainActor的指定,并且是受保护的。对score没有并发访问,所以这是正确的。

实际上,这适用于所有的actor,但我认为编译器存在一个bug,使事情不完全按照你的期望行事。以下代码会编译通过:

actor A {
    var actorVar = 1
    func capture() {
        var localVar = 1
        Task {
            print(actorVar)
            print(localVar)
        }
    }
}

然而,如果你移除对actorVar的引用,传递给Task的闭包将不会放入actor的上下文中,这将使对localVar的引用无效。在我看来,这是一个编译器的bug

英文:

This is a special power of @MainActor. In a View, body is tagged MainActor:

@ViewBuilder @MainActor var body: Self.Body { get }

Tasks inherit the context of their caller, so those are are also MainActor. If you replace Task with Task.detached in body, you will see the same error, since this will move the Task out of the MainActor context.

Conversely, if you add @MainActor to printScore it will also compile without errors:

@MainActor func printScore() async {
    var score = 1

    Task { print(score) }
    Task { print(score) }
}

score inherits the MainActor designation, and is protected. There is no concurrent access to score, so this is fine and correct.

This actually applies to all actors, but I believe the compiler has a bug that makes things not quite behave the way you expect. The following code compiles:

actor A {
    var actorVar = 1
    func capture() {
        var localVar = 1
        Task {
            print(actorVar)
            print(localVar)
        }
    }
}

However, if you remove the reference to actorVar, the closure passed to Task will not be put into the actor's context, and that will make the reference to localVar invalid. IMO, this is a compiler bug.

huangapple
  • 本文由 发表于 2023年2月19日 06:00:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75496651.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定