When I implemented ring buffer with Golang, I encountered one problem: in some situations, one goroutine would occupy CPU forever, and blocked other goroutines. Here is one simple example code:
In the main function it would create 1,000 goroutines to invoke runForever, and in runForever it would print goroutine’s id.
At first, I expected that it would print id 1,000 times in range 0 to 999 immediately, but it only printed id 4 times (equals to the number of GOMAXPROCS).
Finally, I figured out why: it’s because that goroutine scheduling does not depend on thread scheduling in OS.
For example, the threads in Java are the native threads, so when would the CPU context switch depends on job scheduling in OS (Usually, OS would arrange CPU to select one thread to run period of time and it would switch to another thread).
But Golang does implement its own scheduler. I observed that when the goroutine invoke functions, it may trigger the context switch. Using above code as an example, When I put some function like sleep or print in function runForever, it would print id 1,000 times as I expected at first.
But that kind of solution is awful. Golang provides the solution more graceful: runtime.Gosched(). When the goroutine invokes that function, it would yield the processor, allowing other goroutines to run.
here is an example code:
It’s an interesting thing to discover that the goroutines may block each other in some situations like the sample code in this article.
Hopefully, it could help someone like me who is learning Golang.