Golang select 详解

时间:2022-04-25 01:22:23


阅读目录

  • 阐述
  • Golang select 基本用法
  • Golang select 典型用法
  • 阻塞
  • 执行默认分支
  • 执行分支
  • 多个分支同时满足
  • 超时机制
  • select 和 for 循环使用
  • Golang select 使用总结
  • 调用 panic 秒钟调用一次 proc 函数

阐述

golang 中的 select 就是用来监听和 channel 有关的 IO 操作,当 IO 操作发生时,触发相应的动作。

select 只能应用于 channel 的操作,既可以用于 channel 的数据接收,也可以用于 channel 的数据发送。

如果 select 的多个分支都满足条件,则会随机的选取其中一个满足条件的分支执行。

Golang select 基本用法

语法

select {
	case <- chan1:
		// 如果 chan1 成功读到数据,则进行该 case 处理语句
	case chan2 <- 1:
		// 如果成功向 chan2 写入数据,则进行该 case 处理语句
	default:
		// 如果上面都没有成功,则进入default处理流程
}

说明

select 里面即可以对 channel 进行读取,还可以对 channel 进行写入,如果所有条件都不满足,并且有 default 子句,则执行 default 子句,否则,会阻塞。

Golang select 典型用法

阻塞

所有条件都不满足,则永久阻塞。

package main

import (
	"fmt"
)

func main() {
	fmt.Println("知其黑,受其白
	ch := make(chan int, 1)
	select {
	case <-ch:
		fmt.Println("Received from ch")
	}
}

程序运行后,输出如下图所示:

Golang select 详解


我们看到,此时,程序死锁了,因为 select 中永远不可能有满足的条件,即,select 会永久阻塞。

执行默认分支

如果有 default 分支,并且所有条件都不满足,则执行 default 分支。

package main

import (
	"fmt"
)

func main() {
	fmt.Println("知其黑,受其白
	ch := make(chan int, 1)
	select {
	case <-ch:
		fmt.Println("Received from ch")
	default:
		fmt.Println("Run in default")
	}
}

程序运行后,输出如下图所示:

Golang select 详解


我们看到,此时,select 中的所有条件都不满足,因此,执行了 default 字句。

执行分支

如果有满足的分支,则执行对应的分支。

package main

import (
	"fmt"
)

func main() {
	fmt.Println("知其黑,受其白
	ch := make(chan int, 1)
	ch <- 1024
	select {
	case val := <-ch:
		fmt.Println("Received from ch, val =", val)
	default:
		fmt.Println("Run in default")
	}
}

程序运行后,输出如下图所示:

Golang select 详解


我们看到,此时,select 中的第一个分支与 default 分支同时都满足,执行了第一个分支。

多个分支同时满足

如果多个分支同时满足,则随机选择执行

package main

import (
	"fmt"
)

func main() {
	fmt.Println("知其黑,受其白
	ch := make(chan int, 1)
	ch <- 1024
	select {
	case val := <-ch:
		fmt.Println("Received from ch1, val =", val)
	case val := <-ch:
		fmt.Println("Received from ch2, val =", val)
	case val := <-ch:
		fmt.Println("Received from ch3, val =", val)
	default:
		fmt.Println("Run in default")
	}
}

程序运行后,输出如下图所示:

Golang select 详解


我们多运行几次,发现,第一个 case、第二个 case 和第三个 case 都会被执行,即,此时所有条件都满足,则随机选择一个 case 执行。

超时机制

select 可以用于控制超时。

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("知其黑,受其白
	timeout := make(chan bool, 1)
	ch := make(chan int)
	go func() {
		time.Sleep(2 * time.Second)
		timeout <- true
	}()
	select {
	case <-ch:
		fmt.Println("received from ch")
	case <-timeout:
		fmt.Println("select timeout")
	}
}

程序运行后,输出如下图所示:

Golang select 详解

这里,我们使用了 sleep 模拟了超时。

select time.After

select 可以用 time.After 控制超时。

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("知其黑,受其白
	ch := make(chan int)
	select {
	case <-ch:
		fmt.Println("received from ch")
	case <-time.After(time.Second * 2):
		fmt.Println("select timer after timeout")
	}
}

程序运行后,输出如下图所示:

Golang select 详解


这里,我们使用了 time.After 实现了超时控制。

select 和 for 循环使用

可以在 for 循环里面使用 select。

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("知其黑,受其白
	c1 := make(chan string)
	c2 := make(chan string)
	go func() {
		time.Sleep(time.Second * 1)
		c1 <- "one"
	}()
	go func() {
		time.Sleep(time.Second * 2)
		c2 <- "two"
	}()
	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}
}

程序运行后,输出如下图所示:

Golang select 详解

我们在 for 循环里面使用了 select。

Golang select 使用总结

golang 中的 select 就是用来监听和 channel 有关的 IO 操作,当 IO 操作发生时,触发相应的动作。

select 只能应用于 channel 的操作,既可以用于 channel 的数据接收,也可以用于 channel 的数据发送。

如果 select 的多个分支都满足条件,则会随机的选取其中一个满足条件的分支执行。

调用 panic 秒钟调用一次 proc 函数

package main

import (
	"fmt"
	"time"
)

func proc() {
	panic("ok")
}

func main() {
	go func() {
		// 1 在这里需要你算法
		// 2 要求每秒钟调用一次 proc 函数
		// 3 要求程序不能退出
		t := time.NewTicker(time.Second)
		// fmt.Println(time.Now())
		for {
			select {
			case <-t.C:
				go func() {
					defer func() {
						if err := recover(); err != nil {
							fmt.Println(err)
						}
					}()
					fmt.Println(time.Now())
					proc()
				}()
			}
		}
	}()

	select {}
}
running...
2022-12-29 17:12:04.1589622 +0800 CST m=+1.011997801
ok
2022-12-29 17:12:05.1567679 +0800 CST m=+2.009803501
ok
2022-12-29 17:12:06.1537221 +0800 CST m=+3.006757701
ok
2022-12-29 17:12:07.1562807 +0800 CST m=+4.009316301
...