请教一个goroutines问题

下面代码片段,为什么执行到counter3000多,就不执行了。另外如果去掉fmt.Println("counter in main:", counter)的注释,就可以执行完毕。这是为什么啊?


09-02 update:是在main()中第二个for循环卡死了,就是感觉goroutines没有执行完或者没有按预期执行(预期最后counter值会为9000)。但是去掉注释就不会卡死。另外今天在公司电脑,这个偶尔会卡死,偶尔又不会卡死,不知道为什么。

package main

import (
    "fmt"
    "sync"
)

var counter int = 0

func Count(i int, lock *sync.Mutex) {
    lock.Lock()
    fmt.Println("Before Count()", i, counter)
    counter++
    fmt.Println("After Count()", i, counter)
    lock.Unlock()
}

func main() {
    lock := &sync.Mutex{}
    for i := 0; i < 9000; i++ {
        go Count(i, lock)
    }

    for {
        // fmt.Println("counter in main:", counter)
        if counter >= 9000 {
            break
        }
    }
}
阅读 3.5k
3 个回答

恰好研究过这个问题,我来尝试回答一下吧。

这属于golang调度器的问题。截止到目前版本(1.5),golang的调度器是不完全抢占的,也就是说并不是在任何情况下都能够触发抢断。只有当阻塞操作(IO、channel等)、系统调用、非内联函数调用或者显式调用runtime.Gosched()时才能够触发调度,而且即使能够触发调度也不一定真的会发生调度,后台的sysmon会查看当前goroutine的运行时间,过长才会进行调度。

如果去掉了fmt.Println,第二个循环就处于没有调度机会的状态,这个goroutine会永远占用时间片。

你说的无法稳定复现的问题,因为golang版本不同、机器是否多核、调度顺序不同都会发生影响,所以确实是有一些随机性的。

一些详情可以看这篇文章:http://cholerae.com/2015/05/09/%E5%85%B3%E4%BA%8Egoroutine%E8%B0%83%E5%BA%A6%E6%97%B6%E6%9C%BA%E7%9A%84%E8%AF%95%E9%AA%8C/

试着跑了一遍,在main中第二个for循环中卡死了,而且之前的goroutine也没有开始执行。换成sleep就能跑,并且没有遇到题主说的问题。求大神解释。

程序在执行 fmt.Println 的时候有 IO 操作,9000 个 goroutine 有条件在这个 IO 期间完成执行。

没有这个 IO 操作,第二个 for 循环就和 9000 个 goroutine 一直在竞争 counter

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题