转到文档说:
当一个协程块,例如通过调用一个阻塞系统调用,运行时自动地移动在同一操作系统线程其他协程到一个不同的,可运行的线程,以便它们不会被阻塞
但如何运行检测的goroutine被阻止?
例如,如果我会去例程的一个运行计算将其评价为阻塞操作?
package main
import (
"fmt"
"runtime"
)
func f(from string, score int) {
for i := 0; i < score; i++ {
for z := 0; z < score; z++ {
}
}
fmt.Println(from, " Done")
}
func main() {
runtime.GOMAXPROCS(1)
f("direct", 300000)
go f("foo", 200000)
go f("bar", 20000)
go f("baz", 2000)
go func(msg string) {
fmt.Println(msg)
}("going without loop")
var input string
fmt.Scanln(&input)
fmt.Println("done")
}
我得到的结果:巴兹,嘘吧。但为什么?是否转到明白foo
阻止?
这张票是相关的问题:
https://github.com/golang/go/issues/11462
你可以做的每个阻塞call,将被运行时服务。因此,运行时知道如果事情发生这可能阻塞。
例如:
Lock()
在sync.Mutex
运行时会处理这一点,检查阉会阻止或不采取相应的行动。Run()
致电Output()
或exec.Cmd
(或类似)运行时注意到,并假定,这个调用将阻塞。它不能知道无论您正在运行将阻止该程序,所以它必须承担最坏的打算。据我所知,有两个主要的机制,如何够程可以阻止和上面的例子是为每一个例子。
syscall
去(据我所知),这里的运行时间有单钩得到通知会发生什么。当然画有点浑浊,因为它可能是,该golang从OS为即使对于1横OS线程同步需要互斥,然后将其还以某种方式有点例2的。
就问题的代码:否。不理解使得f可能需要大量的时间,如果循环不被编译器优化掉。而在这样一个紧密的循环中,去调度不能“停止”的够程,并设置另一个与运行,因为在循环中没有抢占点。所以,如果你有这样的够程的多张做紧密循环不抢占点,他们可能会吃了你的CPU和所有其它够程必须等待,直到紧密的循环中的至少一个已经完成。但是,只有在调用该循环不同的功能改变了图片,是一个函数调用是一个抢占点。
user1432751问评论:
用什么CPU寄存器时发生阻塞操作是怎样呢?当前线程被阻塞够程,去调度创建新的系统线程,并迁移所有其他线程呢? -
转到时间表是不可抢占(至少是国家我最后一次检查),但在某些抢占点计划。例如系统调用,发送和等待的渠道,如果我没有记错的函数调用。因此,在这些点的内部函数被调用,CPU寄存器相应和堆栈上的够程执行已代码,当调度程序决定该怎么做。
是的,如果一个系统调用完成,因此有被阻止的操作系统线程的危险,该够程做系统调用都有自己的操作系统线程,甚至不指望GOMAXPROCS。
当一个够程已要求运行时做的工作,通常是通过系统调用在这种情况下阻塞通常发生。听一个插座上,例如 - 的够程告诉系统的kqueue / epoll的部分当数据到达,此时它是非常明显受阻将其唤醒。
够程的一个通道上等待消息时,还封锁 - 这里的够程已明确告知,它不会,直到它收到一个消息,在做别的运行,所以运行时知道的很清楚,够程受阻。
大多数操作表单的“做到这一点,给我一个结果”或“给我这个当它准备”都是可核查的阻塞操作,并进入休眠状态,直到准备。
我几个月前得到了同样的问题,我解决它补充说,应该在更短的一定时间进行更新,像看门狗通道:
out := make(chan bool)
go func() {
for {
// Do some stuff here
time.Sleep(10 * time.Millisecond) // leaves CPU free for 10ms
out <- true
}
}()
// main loop
for {
select {
case <-out: // Jump here when out channel is filled
case <-time.After(10 * time.Second): // Jump here if there is no channel activity for 10s
}
}
我希望这将是对你有用。