// 借用例子
package main
import (
"fmt""time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data := []field{{"one"},{"two"},{"three"}}// 例一for _,v := range data {// 解决办法:添加如下语句// v := v
go v.print()}// goroutines print: three, three, three// for 里面的 v := v 是解决这问题的一种方案
time.Sleep(3 * time.Second)
// 注意data2是指针数组
data2 := []*field{{"one"}, {"two"}, {"three"}}
// 例二for _, v := range data2 {// go执行是函数,函数执行之前,函数的接受对象已经传过来
go v.print()
}// goroutines print: one, two, three
time.Sleep(3 * time.Second)
}
上面的代码复制于网络后并在注释上进行了新增,关于例一为什么会出来这种现象,也看了下网上的答案,答案不少,大部分都只是提到了类似 for
是 copy 方式或者 for
里面赋值给另一个变量等,这两种类似的解释都不完善或者说只讲到了一部分
例一原因解析:
1、赋值:for _,v := range data {...}
变量 v 是通过赋值的方式得到
2、条件作用域:for _,v := range data {...}
for 到 { 之间的变量为条件作用域,条件作用域也可叫隐式作用域
3、局部作用域:for {}
,花括号里面的变量为局部作用域
// 例一
data := []*field{{"one"},{"two"},{"three"}}for _,v := range data {// 解决办法:添加如下语句
go v.print()}// goroutines print: three, three, three
// 核心知识点:变量作用域、变量地址与值的关系// go v.print() 这里是调用一个协程,非阻塞。因程序 go v.print() 变量 v 使用的是条件作用域的变量(局部变量调用父级变量,循环第一次的v与循环n次后的v都是同一个变量同一地址),结合原因 1,后赋值覆盖前面赋值,所以 go v.print() 结果是取决于 go 这个协程与 for-range 之间的执行速度,如果协程的执行速度远快于 for-range 一次的速度,那么 for-range 每次循环 go v.print() 的结果都是当前 for-range v 的值,如果 for-range 一次的速度远快于一次协程的速度,那么 for-range 每次循环 go v.print() 的值有可能是当前 for-range 的值也可能会是后面 for-range v 的值,例子:假设 for-range 的速度是 go v.print() 的 10 倍,data 里面有 1-20 个 int, len 为 20 的切片,go v.print() 的结果是前面 10 个为 10,后面 10 个为 20
回到家以后,给还在公司的女朋友发个微信,给你准备了个礼物(家就是个地址),刚开始准备的玫瑰,想到女朋友对荷花也非常喜欢,又换成了荷花,想到她也非常喜欢月季,又换成了月季,送那么比较好呢?换了不知多少次,到换成了项链时,她屁颠屁颠的回来了,这时她收到的礼物是项链。在这个过程中,地址始终未变,礼物一直在变化,女朋友收到的礼物解决于她到家时间(礼物是变量,家为礼物的地址,月季或玫瑰或项链为礼物的值)