The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
ooToo

golang for loop 中的 gocoroutine 的问题

  •  
  •   ooToo · Jul 5, 2019 · 4163 views
    This topic created in 2505 days ago, the information mentioned may be changed or developed.

    刚开始用 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 replies    2019-07-05 13:31:51 +08:00
    skadi
        1
    skadi  
       Jul 5, 2019
    go func(num int){
    // print after create
    }(i)
    ruin2016
        2
    ruin2016  
       Jul 5, 2019
    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
       Jul 5, 2019
    @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  
       Jul 5, 2019   ❤️ 1
    go 的命令可以理解为生成 goroutine 包含一个上下文,把 i 引入了上下文中。当 goroutine 需要运行时,才会调用上下文中的 i 的值。此时可能 i 已经变了。创建 goroutine 到运行 goroutine 总会有时间差的,显然 for 循环一般比调度协程要快得多。
    impl
        5
    impl  
       Jul 5, 2019 via Android
    另一种写法
    for i := 0; i < 10; i++ {
    i := i
    go func() {
    fmt.Printf("go i=%d\n", i)
    }()
    }
    iceheart
        6
    iceheart  
       Jul 5, 2019 via Android   ❤️ 1
    c++可以指定捕获方式是传值还是引用,其他多数语言闭包捕获都是只传引用。
    BruceAuyeung
        7
    BruceAuyeung  
       Jul 5, 2019 via Android   ❤️ 2
    go 创建协程时,只求了方法入参的值,方法体里面的变量引用在代码执行到那里时才运算
    for 循环中的 i 是多次迭代共享的,每次迭代会覆盖旧直值
    所以当协程实际跑到访问 i 变量时,都不知道迭代到哪个地方了,值是不确定的
    webee
        8
    webee  
       Jul 5, 2019   ❤️ 1
    i 只初始化一次,作用域是 for 这个 block.
    且 go routine 在大多数情况下遇到阻塞时都会放弃执行,所以 for loop 结束时,那些新起的 go routine 才开始被调度。
    这种情况下可以通过加参数或者在 for loop block 中新建变量来解决,这叫捕获循环变量。
    在使用 ide 的时候,go vet 会对这种情况有提示。
    ScepterZ
        9
    ScepterZ  
       Jul 5, 2019
    输出运行到 print 时候的值,一般因为循环的快,go 的慢,会出来全是 9
    gamexg
        10
    gamexg  
       Jul 5, 2019   ❤️ 1
    上面说的比较清楚了,go 关键字起的函数并不能保证立刻启动,大概率是 for 结束后才启动,这样造成打印时不能保证 i 的值是什么了。

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


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

    需要在 go 语句前加一句 i := i,创建一个独立的变量。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5898 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 88ms · UTC 02:31 · PVG 10:31 · LAX 19:31 · JFK 22:31
    ♥ Do have faith in what you're doing.