为何在Goroutine中传递函数作参数时变量值有时不符合预期
问题分析:Go闭包的变量捕获机制
先看你贴的代码:
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,核心原因是闭包捕获的是变量的引用,不是变量的当前值。
- main函数里的
rewardAmount是一个栈变量,匿名函数func() { printRewardAmount(rewardAmount) }作为闭包,并没有在创建时复制rewardAmount的数值1,而是保存了指向这个变量的引用。 - goroutine启动后和main goroutine并发执行,main里的代码执行速度极快,几乎会立刻把
rewardAmount改成2。当goroutine里的闭包最终执行printRewardAmount(rewardAmount)时,它会读取rewardAmount的当前值——这时候大概率已经是2了。 - 偶尔输出1只是因为goroutine的调度碰巧比main里的
rewardAmount = 2执行得快,但这种情况完全不可靠,属于典型的竞态条件。
如果要确保输出1,需要在创建闭包时就把当前值传递进去,比如修改成这样:
go WithRecover(func(amount int) { printRewardAmount(amount) }(rewardAmount))
这样会把当时的rewardAmount值1作为参数传给匿名函数,形成值传递,后续修改rewardAmount不会影响这个参数的值。
内容的提问来源于stack exchange,提问作者dev1417
相关产品推荐
相关产品推荐

