Golang: Block forever
Table of contents
Sometimes, you want to block the current goroutine when allowing others to continue. Here is some tricks I’ve collected:
References
Firstly give them some credits:
- https://blog.sgmansfield.com/2016/06/how-to-block-forever-in-go/
- https://pliutau.com/different-ways-to-block-go-runtime-forever/
NOTE: I run these with Golang 1.12
The original
package main
import "fmt"
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(1000)
fmt.Println(i)
}
}
func main() {
go show() // The main goroutine is exited before the show() be done.
fmt.Println("OK we're done")
}
Bad - An empty infinite loop
package main
import (
"fmt"
"time"
)
func forever() {
for {
// Empty, just do nothing
}
}
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(5 * time.Second)
fmt.Println(i)
}
}
func main() {
go show()
forever()
fmt.Println("OK we're done")
}
An infinite loop here is a busy loop that does nothing except burn CPU time.
Good - Busy blocking
package main
import (
"fmt"
"runtime"
"time"
)
func forever() {
for {
runtime.Gosched()
}
}
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(5 * time.Second)
fmt.Println(i)
}
}
func main() {
go show()
forever()
fmt.Println("OK we're done")
}
It will reduce your CPU usage but it isn’t the preferable solution.
Good - Waiting on itself
We wait but we never done XD
package main
import (
"fmt"
"sync"
"time"
)
func forever() {
wg := sync.WaitGroup{}
wg.Add(1)
wg.Wait()
}
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(5 * time.Second)
fmt.Println(i)
}
}
func main() {
go show()
forever()
fmt.Println("OK we're done")
}
Good - Empty select
package main
import (
"fmt"
"time"
)
func forever() {
select{ }
}
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(5 * time.Second)
fmt.Println(i)
}
}
func main() {
go show()
forever()
fmt.Println("OK we're done")
}
Good - Double locking
package main
import (
"fmt"
"sync"
"time"
)
func forever() {
m := sync.Mutex{} // Same with sync.RWMutex
m.Lock()
m.Lock()
}
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(5 * time.Second)
fmt.Println(i)
}
}
func main() {
go show()
forever()
fmt.Println("OK we're done")
}
Good - Reading an Empty Channel
package main
import (
"fmt"
"time"
)
func forever() {
c := make(chan struct{})
<-c
}
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(5 * time.Second)
fmt.Println(i)
}
}
func main() {
go show()
forever()
fmt.Println("OK we're done")
}
Good - Self produce-and-consume
package main
import (
"fmt"
"time"
)
func forever() {
c := make(chan struct{}, 1)
for {
select {
case <-c:
case c <- struct{}{}:
}
}
}
func show() {
for i := 1; i < 9696969; i++ {
time.Sleep(5 * time.Second)
fmt.Println(i)
}
}
func main() {
go show()
forever()
fmt.Println("OK we're done")
}