Go问答 下面这段代码为什么最后会卡主输出?

z54123321 · 2020年03月03日 · 最后由 h12 回复于 2020年03月03日 · 573 次阅读
func main() {
    n := 0
    go func() {
        for {
            log.Println(n)
            n++
        }
    }()
    for {
        if n > 1000000 {
            break
        }
    }
}

而下面这段代码可以跑完

func main() {
    n := 0
    go func() {
        for {
            log.Println(n)
            n++
        }
    }()
    for {
        if n > 100000 {
            break
        }
    }
}

(go version go1.13.5 windows/amd64) 请比较两者代码予以说明

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio

需要提供你的 go sdk 的版本。理论上 for 死循环后,要看 go routine 是怎么调度的。

  1. 以上代码并发读写变量没加任何同步,是错误的写法(虽然不影响问题本身的讨论)
  2. Go language spec 没有规定 goroutine 应该如何调度,所以在代码逻辑没问题的前提下 goroutine 卡住是 Go 1.13 及以前版本 runtime 实现上的局限,而不是语言层面的问题
  3. 这个问题在 Go 1.14 已经得到了解决

以下例子在 go1.14 darwin/amd64 可以跑完,但是在 go1.13.8 会卡住。在适当的位置添加 runtime.Gosched() 可以缓解 1.13 之前的问题。

package main

import (
    "fmt"
    // "runtime"
    "sync/atomic"
)

func main() {
    n := int64(0)
    go func() {
        for i := 0; ; i++ {
            v := atomic.LoadInt64(&n)
            fmt.Println(v)
            atomic.StoreInt64(&n, v+1)
        }
    }()

    prev := atomic.LoadInt64(&n)
    dupCount := 0
    for {
        cur := atomic.LoadInt64(&n)
        if cur == prev {
            dupCount++
            if dupCount > 250000 {
                // uncomment this line to run smoothly before Go 1.14
                // runtime.Gosched()
            }
        }
        if cur > 1000000 {
            break
        }
    }
}
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册