我正在使用一个开源包,它利用runtime.Gosched()
来等待资源可用。并发现可能会导致CPU使用率过高。代码可以简化为如下所示:
func wait() {
for {
// check if something is available, and return it when available.
// if size > oldSize {
// return buffer[oldSize:size-1]
// }
// In my case there's no write to the buffer for quite a long time
// so that it keep arriving at this Gosched calling
runtime.Gosched()
}
}
func run() {
for i := 0; i < 20; i++ {
go wait()
}
time.Sleep(time.Second*20)
}
我观察到在运行 run(( 函数时它占用了所有可用的 CPU 资源。但是,在使用通道时,如以下代码所示,不存在此类问题。
func waitForChannel(ch chan struct{}) {
<- ch
}
func runWithChannel() {
ch := make(chan struct{})
for i := 0; i < 20; i++ {
go waitForChannel(ch)
}
time.Sleep(time.Second*20)
//close(ch)
}
我知道当我想等待资源并产生当前处理器时,我应该使用通道。但是,我找不到等待频道和一遍又一遍地打电话给Gosched的区别的解释。Gosched的用例是什么,如果它不适合这种情况。
运行时的文档。戈舍德 说:
Gosched 产生处理器,允许其他 goroutines 运行。它不会挂起当前的 goroutine,因此执行会自动恢复。
这表明发生的情况是,您可能会突破一个 Go 例程的for
循环,但立即进入另一个 Go 例程的for
循环。因此,CPU仍然忙于运行大量for
循环,无法喘口气。
在等待另一个资源时,我通常会使用一个简单的time.Sleep
。即使是最短的持续时间也会使示例的 CPU 使用率降至 0%(任务管理器说(:
package main
import "time"
func wait() {
for {
time.Sleep(1)
}
}
func main() {
for i := 0; i < 20; i++ {
go wait()
}
time.Sleep(time.Second * 20)
}
请注意,您通常会说类似
time.Sleep(time.Millisecond)
或其他一些持续时间。我只是想说明,即使是最小的超时也会让 CPU 呼吸。
运行时。Gosched 函数生成处理器,以便其他 goroutines 可以运行。它不会挂起调用 goroutine。
但是,我找不到等待频道和一遍又一遍地打电话给Gosched的区别的解释。
通道上的接收会暂停接收 goroutine,直到值可用或通道关闭。
对 Gosched 的调用会生成处理器,以便其他 goroutines 可以运行,但它不会挂起 goroutine。go例程中的循环继续旋转并消耗 CPU 资源。
Gosched的用例是什么,如果它不适合这种情况。
很少有程序需要调用Gosched。
通道发送和接收、互斥锁和解锁、I/O等操作都可以产生处理器。这些屈服点通常足以满足应用需求。
在长时间运行的操作中需要 Gosched 调用,否则不会生成处理器。例如,不执行任何 I/O 的长时间运行的数值计算。