我试图更好地理解 goroutines 在 Go 程序中是如何调度的,特别是在什么时候它们可以让步给其他 goroutines。我们知道 goroutine 会在 syscals 上产生会阻塞它,但显然这不是全部。
这个问题引起了一些类似的担忧,最受好评的答案说 goroutine 也可以打开函数调用,因为这样做会调用调度程序来检查堆栈是否需要增长,但它明确表示
如果您没有任何函数调用,只是一些数学运算,那么是的,goroutine 将锁定线程,直到它退出或遇到可以让其他人执行的东西。
我写了一个简单的程序来检查和证明:
package main
import "fmt"
var output [30]string // 3 times, 10 iterations each.
var oi = 0
func main() {
runtime.GOMAXPROCS(1) // Or set it through env var GOMAXPROCS.
chanFinished1 := make(chan bool)
chanFinished2 := make(chan bool)
go loop("Goroutine 1", chanFinished1)
go loop("Goroutine 2", chanFinished2)
loop("Main", nil)
<- chanFinished1
<- chanFinished2
for _, l := range output {
fmt.Println(l)
}
}
func loop(name string, finished chan bool) {
for i := 0; i < 1000000000; i++ {
if i % 100000000 == 0 {
output[oi] = name
oi++
}
}
if finished != nil {
finished <- true
}
}
注意:我知道在数组中放置一个值并在oi不同步的情况下递增并不完全正确,但我希望保持代码简单且没有可能导致切换的东西。毕竟,可能发生的最糟糕的事情是在不推进索引(覆盖)的情况下设置一个值,这没什么大不了的。
与这个答案不同,我避免使用作为 goroutine 启动的函数中的任何函数调用(包括内置append())loop(),而且我GOMAXPROCS=1根据文档明确设置:
限制可以同时执行用户级 Go 代码的操作系统线程数。
尽管如此,在输出中我仍然看到消息Main//交错Goroutine 1,Goroutine 2这意味着以下之一:
goroutine 的执行中断并且 goroutine 在某些时刻放弃了控制;
GOMAXPROCS不像文档中所说的那样工作,启动更多的操作系统线程来调度 goroutine。
要么答案不完整,要么自 2016 年以来有些事情发生了变化(我在 Go 1.13.5 和 1.15.2 上进行了测试)。
如果这个问题得到了回答,我很抱歉,但我既没有找到解释为什么这个特定的例子会产生控制,也没有找到 goroutines 一般产生控制的点(阻塞系统调用除外)。
注意:这个问题纯粹是理论上的,我现在不打算解决任何实际任务,但总的来说,我假设知道 goroutine 可以产生和不能产生的点可以让我们避免同步原语的冗余使用。
DIEA
开满天机
相关分类