Golang: Block forever

Golang: Block forever

Apr 27, 2020
#golang #trick #note

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:

  1. https://blog.sgmansfield.com/2016/06/how-to-block-forever-in-go/
  2. 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")
}