在 goroutine 中将函数作为参数传递

Passing function as an argument in goroutine

提问人:dev1417 提问时间:9/4/2023 更新时间:9/4/2023 访问量:102

问:

import (
    "fmt"
    "time"
)

func WithRecover(r func()) {
    r()
}

func printRewardAmount(rewardAmount int) {
    fmt.Println(rewardAmount)
}

func main() {
    var rewardAmount int
    rewardAmount = 1
    go WithRecover(func() { printRewardAmount(rewardAmount) })
        if true {
        rewardAmount = 2
        }
    time.Sleep(100 * time.Millisecond)
}

当我们将 rewardAmount 作为值传递时,输出应为 1。但是我有时会得到 2 个,有人可以解释我为什么吗?

函数 go 参数 goroutine

评论

4赞 icza 9/4/2023
您的代码从多个 goroutines 进行访问,其中一个访问是写入。这是一场数据竞赛。您必须同步对 的访问。rewardAmountrewardAmount
0赞 dev1417 9/4/2023
但是我们将 rewardAmount 作为值传递,而不是作为引用
1赞 icza 9/4/2023
当你传递它时,你必须读取它的值才能知道要传递什么。goroutine 编写了它。main
0赞 dev1417 9/4/2023
goroutine 何时在创建或执行时读取值?因为在将值设置为 2 之前将创建 goroutine
0赞 Peter 9/4/2023
它是在执行 goroutine 期间读取的。如果你改写,情况会有所不同;那么就没有比赛了。go printRewardAmount(rewardAmount)

答:

2赞 chinmayan 9/4/2023 #1
go WithRecover(func() { printRewardAmount(rewardAmount) })

在上面的代码中,您只是将 的值仅传递给函数,而不是传递给 go 例程。因此,范围仍然在 go 例程之外(inside )。因此,根据 go 例程的执行顺序,输出可以是 1 或 2。rewardAmountprintRewardAmount()rewardAmountmain()

只需通过替换为以下内容来查看地址即可rewardAmountmain()

func main() {
    var rewardAmount int
    rewardAmount = 1
    fmt.Println("address of rewardAmount in main:", &rewardAmount)
    go WithRecover(func() {
        fmt.Println("address of rewardAmount inside go routine:", &rewardAmount)
        printRewardAmount(rewardAmount)
    })
    if true {
        rewardAmount = 2
    }
    time.Sleep(100 * time.Millisecond)
}

Playground 中的完整代码

但是,当您执行以下操作时,您实际上是在复制 的值并将其传递给 go 例程。因此,输出将始终为 1。rewardAmount

func main() {
    var rewardAmount int
    rewardAmount = 1
    fmt.Println("address of rewardAmount in main:", &rewardAmount)
    go func(rewardAmount int) {
        fmt.Println("address of rewardAmount inside go routine:", &rewardAmount)
        WithRecover(func() {
            printRewardAmount(rewardAmount)
        })
    }(rewardAmount)
    if true {
        rewardAmount = 2
    }
    time.Sleep(100 * time.Millisecond)
}

Playground 中的完整代码