V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
thomaswang
V2EX  ›  问与答

这段 go 代码如何理解

  •  
  •   thomaswang · 2019-04-25 17:48:00 +08:00 · 2566 次点击
    这是一个创建于 1799 天前的主题,其中的信息可能已经有所发展或是发生改变。
    for i := 0; i < 10; i++ {
    	go func() {
    		fmt.Println("i: ", i)
    		wg.Done()
    	}()
    }
    

    这个新开的 goroutein 应该是一个新的函数栈吧, 为什么它可以拿到 i 呢,

    21 条回复    2019-04-30 11:34:57 +08:00
    rrfeng
        1
    rrfeng  
       2019-04-25 17:56:42 +08:00
    搜一下就可以了:go routine 变量继承

    go 在编译时进行逃逸分析,发现 i 被使用就放到堆里去了。如果没使用就放栈里。
    Reficul
        2
    Reficul  
       2019-04-25 17:57:10 +08:00
    闭包呀
    bestkayle
        3
    bestkayle  
       2019-04-25 18:42:32 +08:00 via iPhone
    @rrfeng #1 是闭包捕获吧
    Vegetable
        4
    Vegetable  
       2019-04-25 18:45:45 +08:00
    下一个问是:fmt 前加上 sleep 1s,打印出来的结果是什么:doge
    gamexg
        5
    gamexg  
       2019-04-25 20:09:42 +08:00
    @Vegetable #4 他这不加 sleep 结果一般也会符合预期的不正确。
    thomaswang
        6
    thomaswang  
    OP
       2019-04-25 20:18:39 +08:00
    @rrfeng 你说的是 func a 调 func b,b 直接把局部变量返回给 a 吗
    beiping96
        7
    beiping96  
       2019-04-25 20:22:43 +08:00
    1. 闭包
    2. 打印的全是 10 (不是 9 )
    xdeng
        8
    xdeng  
       2019-04-25 20:54:29 +08:00
    @beiping96 好点电脑会小点
    JaguarJack
        9
    JaguarJack  
       2019-04-25 20:59:23 +08:00 via iPhone
    @beiping96 应该是不确定的
    thomaswang
        10
    thomaswang  
    OP
       2019-04-25 21:04:46 +08:00
    @rrfeng 你说的也是对的, 必报捕获也是你这个逃逸
    sunjourney
        11
    sunjourney  
       2019-04-25 21:04:52 +08:00
    你的例子确定是愿意吗?
    如果要打印 1 到 9 应该这么写:
    ```go
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
    fmt.Println("i: ", i)
    wg.Done()
    }(i)
    }
    ```
    或者
    ```go
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
    j := i
    wg.Add(1)
    go func() {
    fmt.Println("i: ", j)
    wg.Done()
    }()
    }
    ```
    thomaswang
        12
    thomaswang  
    OP
       2019-04-25 23:53:05 +08:00
    @sunjourney 那你解释一下第二个方法闭包里面为什么可以拿到 i
    mengzhuo
        13
    mengzhuo  
       2019-04-26 00:26:24 +08:00 via iPhone
    1. i 是一个内存上的地址
    2. 当 goroutine 是创建时会*拷贝*一份执行栈上的数据, 但不运行
    3. i=10 时,循环结束主 goroutine 暂停,调度器开始寻找可以执行的 goroutine
    4. 其他 goroutine 的作用域中并没有 i 这个本地变量,开始向上查找
    5. 找到主 goroutine 中的 i , 此时 i =10 并打印。
    whoami9894
        14
    whoami9894  
       2019-04-26 00:31:52 +08:00 via Android
    @mengzhuo
    不太了解 goroutine 的调度机制,如果是多核多调度器是有可能打印出非十个 10 吧
    jadeity
        15
    jadeity  
       2019-04-26 06:22:24 +08:00
    @whoami9894 实际也不是十个 10,这是一个不可预测的结果,如果按照楼主的写法 go vet 会警告的。
    respect11
        16
    respect11  
       2019-04-26 08:57:59 +08:00
    thomaswang
        17
    thomaswang  
    OP
       2019-04-26 09:36:31 +08:00
    @mengzhuo 第三条,主 routine 和各个子 routine 是调度器去调度, 执行顺序没法控制吧, 第 2 条, 拷贝一份执行栈上的数据, 和第四条, 向上查找,有详细的结束资料吗, 我怎么找不到啊
    thomaswang
        18
    thomaswang  
    OP
       2019-04-26 09:36:49 +08:00
    @respect11 ?, 不是我
    respect11
        19
    respect11  
       2019-04-26 09:41:36 +08:00
    @thomaswang #18 刚差点看错。。
    sunjourney
        20
    sunjourney  
       2019-04-26 13:43:47 +08:00
    @thomaswang #12 这还要解释?
    mengzhuo
        21
    mengzhuo  
       2019-04-30 11:34:57 +08:00
    @thomaswang
    注意: 这是*并发*,不是*并行*
    调度顺序没办法预测的

    调度器会在寻找可执行的 goroutine 时,M 会先在 local queue 找(也就是同一个 thread 上),也会随机偷 global queue 里等待执行的 goroutine
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1154 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 23:03 · PVG 07:03 · LAX 16:03 · JFK 19:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.