英文:
Golang: goroutine infinite-loop
问题
当从上面的代码中删除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)
}
当删除fmt.Print()
行时,代码会无限运行的原因是,fmt.Print()
函数在每次循环迭代时都会尝试读取标准输入,但是没有提供任何参数给它,因此它会一直等待输入。由于没有输入,代码就会陷入无限循环,无法继续执行下去。
英文:
When an fmt.Print()
line is removed from the code below, code runs infinitely. Why?
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
// 允许其他goroutine继续执行。
runtime.Gosched()
fmt.Print()
起到了类似的作用,并允许main()
有机会继续执行。
在无限循环的情况下,export GOMAXPROCS=2
可能有助于程序完成,如在“golang: goroute with select doesn't stop unless I added a fmt.Print()
”中所解释的那样。
> fmt.Print()
明确地将控制权传递给一些syscall的内容
> 在之前的版本中,无限循环的goroutine可能会使同一线程上的其他goroutine饿死,当GOMAXPROCS
只提供一个用户线程时,这是一个严重的问题。
> 在Go 1.2中,这部分得到了解决:调度程序在进入函数时会偶尔被调用。这意味着任何包含**(非内联)函数**调用的循环都可以被抢占,从而允许在同一线程上运行其他goroutine。
请注意我强调的部分:在你的示例中,for循环atomic.AddUint64(&ops, 1)
可能已经被内联。在那里没有抢占。
更新于2017年:Go 1.10将摒弃GOMAXPROCS
。
英文:
The Go By Example article includes:
// Allow other goroutines to proceed.
runtime.Gosched()
The fmt.Print()
plays a similar role, and allows the main()
to have a chance to proceed.
A export GOMAXPROCS=2
might help the program to finish even in the case of an infinite loop, as explained in "golang: goroute with select doesn't stop unless I added a fmt.Print()
".
> fmt.Print()
explicitly passes control to some syscall stuff
Yes, go1.2+ has pre-emption in the scheduler
> In prior releases, a goroutine that was looping forever could starve out other goroutines on the same thread, a serious problem when GOMAXPROCS
provided only one user thread.
> In Go 1.2, this is partially addressed: The scheduler is invoked occasionally upon entry to a function. This means that any loop that includes a (non-inlined) function call can be pre-empted, allowing other goroutines to run on the same thread.
Notice the emphasis (that I put): it is possible that in your example the for loop atomic.AddUint64(&ops, 1)
is inlined. No pre-emption there.
Update 2017: Go 1.10 will get rid of GOMAXPROCS
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论