Select简明教程

select关键字到底有什么作用?下面先来一个简单例子说明:

func main(){
    fmt.Println(time.Now().Format("15:04:05") + " Go Select Statement Tutorial")

    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "video data"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "audio data"
    }()

    fmt.Println(time.Now().Format("15:04:05"), "Started goroutine!!")
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <- ch1:
            fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg1)
        case msg2 := <- ch2:
            fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg2)
        }
    }
}

创建两个Channel和两个子协程分别向一个Channel发送信息,执行看看打印日志:

~ » go run main.go
12:33:29 Go Select Statement Tutorial
12:33:29 Started goroutine!!
12:33:30 Received:  video data
12:33:31 Received:  audio data

看日志可以发现主协程在启动完两个子协程,到select关键字的时候发生阻塞了!等待第一个子协程睡眠1s后发送消息到Channel,主协程才继续执行打印日志,再过1s第二个子协程发送消息,主协程继续打印。

那么我们可以得出简单的结论,select关键字有点像阻塞版的switch,不过它只针对Channel,等待Channel有信息返回时可以为不同的Channel触发相应的行为。

我们看下官方有关Select关键字的描述:

A "select" statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations.
一个select语句用来选择哪个case中的发送或接收操作可以被立即执行。它类似于switch语句,但是它的case涉及到channel有关的I/O操作。

或者换一种说法,select就是用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作。

修改上面的基础使用方法我们会看到什么效果呢?

修改一

修改for循环的次数:

  • 修改为1时,for循环次数比子协程次数少,那么在主协程结束前只有一个子协程执行完成,主协程不会等待剩余的子协程。即,一个select关键字只会等待一个Channl

    ~ » go run main.go
    12:49:51 Go Select Statement Tutorial
    12:49:51 Started goroutine!!
    12:49:52 Received:  video data
    
  • 修改为3时,for循环次数比子协程次数多,那么等待完两个Channel的消息后,会引入死锁状态。

    ~ » go run main.go
    12:52:08 Go Select Statement Tutorial
    12:52:08 Started goroutine!!
    12:52:09 Received:  video data
    12:52:09 Finished ch1
    12:52:10 Finished ch2
    12:52:10 Received:  audio data
    fatal error: all goroutines are asleep - deadlock!
    
    goroutine 1 [select]:
    main.main()
            ~/main.go:28 +0x32c
    exit status 2
    
    

修改二

在select所包含的代码块里添加default关键字,看看什么效果:

for i := 0; i < 2; i++ {
        select {
        case msg1 := <- ch1:
            fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg1)
        case msg2 := <- ch2:
            fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg2)
        default:
            fmt.Println(time.Now().Format("15:04:05"), "execute default")
        }
    }

打印日志如下:

~ » go run main.go
12:55:05 Go Select Statement Tutorial
12:55:05 Started goroutine!!
12:55:05 execute default
12:55:05 execute default

发现select关键字再也不阻塞等待Channel返回的,继续执行知道主协程结束。

修改三

在case里加入break关键字,会马上返回,不再继续执行。

for i := 0; i < 2; i++ {
        select {
        case msg1 := <- ch1:
            fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg1)
            break
            fmt.Println(time.Now().Format("15:04:05"), "print2")
        case msg2 := <- ch2:
            fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg2)
            fmt.Println(time.Now().Format("15:04:05"), "print2")
        }
    }

执行日志如下:

~ » go run main.go
13:01:45 Go Select Statement Tutorial
13:01:45 Started goroutine!!
13:01:46 Finished ch1
13:01:46 Received:  video data
13:01:47 Finished ch2
13:01:47 Received:  audio data
13:01:47 print3

可以看到case msg1 := <- ch1:情况发生时,执行完第一句打印,然后遇上break关键字马上停止了下面语句的执行。

午安~

你可能感兴趣的:(Select简明教程)