One article on the four main uses of Go Language Select

Keywords: Programming Go Unix

This article introduces you to the use of Select in the Go Language and believes that you are not unfamiliar with switching. However, select and switching have a common feature that they both handle things in a case way, but select and switching handle things completely different and incompatible.Let's see what switching is like: various types and type operations, interface interface {} type judgment variable.(type), with the emphasis on executing in case order.Let's start with an example:

package main

var (
    i interface{}
)

func convert(i interface{}) {
    switch t := i.(type) {
    caseint:
        println("i is interger", t)
    casestring:
        println("i is string", t)
    casefloat64:
        println("i is float64", t)
    default:
        println("type not found")
    }
}

func main() {
    i = 100
    convert(i)
    i = float64(45.55)
    convert(i)
    i = "foo"
    convert(i)
    convert(float32(10.0))
}

The results are as follows:

i is interger 100
i is float64 +4.555000e+001
i is string foo
type not found

The select has a different feature. It only receives channels, otherwise it will make errors, and default will execute directly. So without default, selects will encounter blocking, assuming that not sending value into Channel will result in panic. Here are a few examples.

Random Select

The same channel is selected randomly in select, so here's an example:

package main

import"fmt"

func main() {
    ch := make(chanint, 1)

    ch <- 1
    select {
    case <-ch:
        fmt.Println("random 01")
    case <-ch:
        fmt.Println("random 02")
    }
}

After execution, you will find that sometimes you get random 01 and sometimes you get random 02, which is one of the characteristics of select. case is random selection, so when select has more than two channels, if you send data to all channels at the same time, you will randomly pick different Channels.Another feature mentioned above is "Assuming that not sending a value into Channel would result in a panic". Take the example above to change it:

func main() {
    ch := make(chanint, 1)

    select {
    case <-ch:
        fmt.Println("random 01")
    case <-ch:
        fmt.Println("random 02")
    }
}

After execution, you will find that it becomes deadlock, which causes the main host to explode, and you can solve this problem directly by default:

func main() {
    ch := make(chanint, 1)

    select {
    case <-ch:
        fmt.Println("random 01")
    case <-ch:
        fmt.Println("random 02")
    default:
        fmt.Println("exit")
    }
}

The main program does not cause the entire program deadlock to fail to read the channel value.

Timeout timeout mechanism

When reading channle with select, you will be sure to do something else after a certain amount of time, instead of blocking all the time inside the select.Here's a simple example:

package main

import (
    "fmt"
    "time"
)

func main() {
    timeout := make(chanbool, 1)
    gofunc() {
        time.Sleep(2 * time.Second)
        timeout <- true
    }()
    ch := make(chanint)
    select {
    case <-ch:
    case <-timeout:
        fmt.Println("timeout 01")
    }
}

Set up a timeout channel so that select execution can end elsewhere via trigger timeout channel, or another way of writing is to hold the time.After mechanism through

select {
case <-ch:
case <-timeout:
    fmt.Println("timeout 01")
case <-time.After(time.Second * 1):
    fmt.Println("timeout 02")
}

Note that time.After returns chan time.Time, so when you execute a select for more than a second, you output timeout 02.

Check if channel is full

It's faster to look directly at the examples:

package main

import (
    "fmt"
)

func main() {
    ch := make(chanint, 1)
    ch <- 1
    select {
    case ch <- 2:
        fmt.Println("channel value is", <-ch)
        fmt.Println("channel value is", <-ch)
    default:
        fmt.Println("channel blocking")
    }
}

First declare a channel with buffer size 1, then drop the value to fill the channel.At this point, you can make sure the channel is full by selecting + default. The above example will output channel blocking, and we will change the program to the bottom

func main() {
    ch := make(chanint, 2)
    ch <- 1
    select {
    case ch <- 2:
        fmt.Println("channel value is", <-ch)
        fmt.Println("channel value is", <-ch)
    default:
        fmt.Println("channel blocking")
    }
}

Changing the buffer size to 2 will allow you to continue entering channels at Se value, where you can see the previous article, "Understanding what unbuffered vs buffered channel is in five minutes" [1]

select for loop usage

If you have more than one channel to read, and reading is uninterrupted, you must use the for + select mechanism. A more detailed implementation can refer to "15 minutes of learning how the Go Language handles multiple Channel Channels"

package main

import (
    "fmt"
    "time"
)

func main() {
    i := 0
    ch := make(chanstring, 0)
    deferfunc() {
        close(ch)
    }()

    gofunc() {
    LOOP:
        for {
            time.Sleep(1 * time.Second)
            fmt.Println(time.Now().Unix())
            i++

            select {
            case m := <-ch:
                println(m)
                break LOOP
            default:
            }
        }
    }()

    time.Sleep(time.Second * 4)
    ch <- "stop"
}

The example above shows the following after execution:

1574474619
1574474620
1574474621
1574474622

In fact, removing default can also achieve the purpose

select {
case m := <-ch:
    println(m)
    break LOOP

When there is no value to feed in, it will stop in the select section all the time, so no default actually works. To end for or select, you need to end through break. But to end for loop directly in the select interval, you can only end with break variable, which is something everyone needs to pay attention to.

Reference material

[1] "Learn what unbuffered vs buffered channel is in five minutes: https://blog.wu-boy.com/2019/04/understand-unbuffered-vs-buffered-channel-in-five-minutes/

Posted by wafawj on Tue, 10 Mar 2020 10:28:21 -0700