I'm using an open source package that leverage the runtime.Gosched()
to wait for a resource to be available. And found it may cause a high CPU usage. The code can be simplified to be like this:
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)
}
I have observed that it took all the CPU resource available when running the run() function. However, when using a channel, as the following code, there is no such problem.
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)
}
I know that I should use channels, when I want to wait for a resource and yield the current processor. However I can't find an explanation of the difference of waiting for a channel and calling Gosched over and over again. And what's the use case of Gosched, if it's not suitable for this situation.
The documentation for runtime.Gosched says:
Gosched yields the processor, allowing other goroutines to run. It does not suspend the current goroutine, so execution resumes automatically.
This suggests that what happens is that you may break out of the for
loop of one Go routine but immediately enter the for
loop of another Go routine. Thus the CPU is still busy running a lot of for
loops, unable to take a breath.
I will usually use a simple time.Sleep
when waiting for another resource. Even the smallest duration will get the CPU usage of your example down to 0% (says the Task Manager):
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)
}
Note that you would usually say something like
time.Sleep(time.Millisecond)
or some other duration. I just wanted to illustrate that even the smallest timeout will give the CPU air to breathe.