golang for loop 中的 gocoroutine 的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
ooToo
V2EX    Go 编程语言

golang for loop 中的 gocoroutine 的问题

  •  
  •   ooToo 2019-07-05 11:56:43 +08:00 3966 次点击
    这是一个创建于 2370 天前的主题,其中的信息可能已经有所发展或是发生改变。

    刚开始用 go, 习惯 Java 了, 不小心踩了一个坑.
    本意是 print 0 到 9 的. 虽然通过加参数解决了, 但是为啥会这样呢?
    fmt.Printf("go i=%d\n", i) 这里面 i 的值是怎么获得的, 和 for 共享吗?
    请指教下, 多谢了

     for i := 0; i < 10; i++ { go func() { fmt.Printf("go i=%d\n", i) }() } 
    11 条回复    2019-07-05 13:31:51 +08:00
    skadi
        1
    skadi  
       2019-07-05 12:02:08 +08:00
    go func(num int){
    // print after create
    }(i)
    ruin2016
        2
    ruin2016  
       2019-07-05 12:12:07 +08:00
    package main

    import (
    "fmt"
    "sync"
    )

    func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
    fmt.Printf("go i=%d\n", i)
    wg.Done()
    }(i)
    }

    wg.Wait()
    }

    go i=9
    go i=5
    go i=6
    go i=7
    go i=8
    go i=2
    go i=3
    go i=0
    go i=1
    go i=4
    ooToo
        3
    ooToo  
    OP
       2019-07-05 12:24:52 +08:00
    @skadi @ruin2016 感谢两位, 其实

    可能没说太清楚, 搞混的和 kotlin
    for (i in 1..10) {
    executor.submit { println(i) }
    }
    kotlin 这样是没有问题的.
    既然 go func() {}() 没有参数 fmt.Printf("go i=%d\n", i) 这里面 i 的值是怎么获得的, 和 for 共享吗?
    还有就是 go 执行顺序
    可能 go scope 和 gocoroutine 机制的问题吧, 有空多研究下吧
    fuxiaohei
        4
    fuxiaohei  
       2019-07-05 12:29:43 +08:00   1
    go 的命令可以理解为生成 goroutine 包含一个上下文,把 i 引入了上下文中。当 goroutine 需要运行时,才会调用上下文中的 i 的值。此时可能 i 已经变了。创建 goroutine 到运行 goroutine 总会有时间差的,显然 for 循环一般比调度协程要快得多。
    impl
        5
    impl  
       2019-07-05 12:30:31 +08:00 via Android
    另一种写法
    for i := 0; i < 10; i++ {
    i := i
    go func() {
    fmt.Printf("go i=%d\n", i)
    }()
    }
    iceheart
        6
    iceheart  
       2019-07-05 12:43:16 +08:00 via Android   1
    c++可以指定捕获方式是传值还是引用,其他多数语言闭包捕获都是只传引用。
    BruceAuyeung
        7
    BruceAuyeung  
       2019-07-05 12:50:40 +08:00 via Android   2
    go 创建协程时,只求了方法入参的值,方法体里面的变量引用在代码执行到那里时才运算
    for 循环中的 i 是多次迭代共享的,每次迭代会覆盖旧直值
    所以当协程实际跑到访问 i 变量时,都不知道迭代到哪个地方了,值是不确定的
    webee
        8
    webee  
       2019-07-05 13:08:01 +08:00   1
    i 只初始化一次,作用域是 for 这个 block.
    且 go routine 在大多数情况下遇到阻塞时都会放弃执行,所以 for loop 结束时,那些新起的 go routine 才开始被调度。
    这种情况下可以通过加参数或者在 for loop block 中新建变量来解决,这叫捕获循环变量。
    在使用 ide 的时候,go vet 会对这种情况有提示。
    ScepterZ
        9
    ScepterZ  
       2019-07-05 13:08:04 +08:00
    输出运行到 print 时候的值,一般因为循环的快,go 的慢,会出来全是 9
    gamexg
        10
    gamexg  
       2019-07-05 13:17:44 +08:00   1
    上面说的比较清楚了,go 关键字起的函数并不能保证立刻启动,大概率是 for 结束后才启动,这样造成打印时不能保证 i 的值是什么了。

    解决办法有给函数加参数,另外也可以这么写。


    for i := 0; i < 10; i++ {
    i:=i
    go func() {
    fmt.Printf("go i=%d\n", i)
    }()
    }
    reus
        11
    reus  
       2019-07-05 13:31:51 +08:00
    i 是同一个变量,所有 goroutine 用到的都是同一个变量,所以你的程序是错的

    需要在 go 语句前加一句 i := i,创建一个独立的变量。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5293 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 99ms UTC 03:18 PVG 11:18 LAX 19:18 JFK 22:18
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86