for range 作用域

// 借用例子
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

回到家以后,给还在公司的女朋友发个微信,给你准备了个礼物(家就是个地址),刚开始准备的玫瑰,想到女朋友对荷花也非常喜欢,又换成了荷花,想到她也非常喜欢月季,又换成了月季,送那么比较好呢?换了不知多少次,到换成了项链时,她屁颠屁颠的回来了,这时她收到的礼物是项链。在这个过程中,地址始终未变,礼物一直在变化,女朋友收到的礼物解决于她到家时间(礼物是变量,家为礼物的地址,月季或玫瑰或项链为礼物的值)

本作品采用《CC 协议》,转载必须注明作者和本文链接
光年之外
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!