Golang: Block forever
Apr 27, 2020
Sometimes, you want to block the current goroutine when allowing others to continue. Here is some tricks I’ve collected:
1. 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
2. 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")
}
3. 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.
4. 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.
5. 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")
}
6. 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")
}
7. 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")
}
8. 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")
}
9. 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")
}