Golang: goroutine无限循环

3
当下面的代码中移除了fmt.Print()行时,代码会无限运行。为什么?
package main

import "fmt"
import "time"
import "sync/atomic"

func main() {
        var ops uint64 = 0 
        for i := 0; i < 50; i++ {
                go func() {
                        for {
                                atomic.AddUint64(&ops, 1)
                                fmt.Print()
                        }
                }()
        }
        time.Sleep(time.Second)
        opsFinal := atomic.LoadUint64(&ops)
        fmt.Println("ops:", opsFinal)
}
1个回答

8

这篇Go By Example文章包括:

   // Allow other goroutines to proceed.
   runtime.Gosched()
fmt.Print()起到了类似的作用,并允许main()有机会继续执行。 export GOMAXPROCS=2可能会帮助程序即使在无限循环的情况下也能完成,正如"golang: goroute with select doesn't stop unless I added a fmt.Print()"中所解释的那样。
fmt.Print()显式地将控制传递给一些syscall stuff。

是的,go1.2+在调度器中具有抢占功能
在之前的版本中,一个永远循环的goroutine可能会使同一线程上的其他goroutine饿死,在GOMAXPROCS只提供一个用户线程的情况下,这是一个严重的问题。在Go 1.2中,部分解决了这个问题:调度器偶尔会在进入函数时被调用。这意味着任何包含一个(非内联)函数调用的循环都可以被抢占,从而允许在同一线程上运行其他goroutine。
请注意我加的强调:在你的例子中,循环atomic.AddUint64(&ops, 1)可能已经被内联了。那里没有抢占。
更新2017:Go 1.10将摆脱GOMAXPROCS

来自golang.org的这段话:在之前的版本中,一个无限循环的goroutine可能会使同一线程上的其他goroutine饿死,当GOMAXPROCS只提供一个用户线程时,这是一个严重的问题。在Go 1.2中,这个问题得到了部分解决:调度程序会在进入函数时偶尔被调用。这意味着任何包含(非内联)函数调用的循环都可以被抢占,从而允许其他goroutine在同一线程上运行。 - xged
@xged,正如https://dev59.com/bWct5IYBdhLWcg3wAo-H#20836784中的评论所述,虽然不在播放场中运行,但它无法工作。 - VonC
@xged 我已经编辑了答案:如果for循环被内联,你就不会有任何抢占。 - VonC
1
@xged 可能是因为 GOMAXPROCS 总是 1,而且由于内联的原因,它在 playground 外部不起作用,所以它也不会在 playground 内部起作用。 - VonC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接