05. 并发选择语句 (select)
select 语句在 Shell 中没有, 但是在 Go 中却是 并发 的根基。
在语法上面, 和 switch 相似
- 使用
case作为分支入口。 - 使用
default作为默认语句。
不同的是,
select没有条件语句
func selectGrammar(ctx context.Context) {
channel := make(chan int, 10)
value := 100
select {
case <-ctx.Done():
// 执行的代码
case <-time.After(10 * time.Second):
// 执行的代码
case val := <-channel:
fmt.Println(val)
// 执行的代码
case channel <- value:
// 执行代码
default:
// 所有通道都没有准备好,执行的代码
}
}
- 每个
case语句都必须是一个通道(channel)。 常见的通道包括以下:
ctx.Done。time.After。- chan 定义的通道
- 所有 通达都会 被求值。 无论 进入或发送
- 如果在进入
select的时候, 已经有 多个分支 的通道 就绪, 则 随机 选择一个。 - 如果任意分支执行了, 其他分支就不执行了。
- 关于
default分支, 其他分支没准备好是
- 当 没有 default 将 阻塞。 由于 阻塞 的特性, 所以有一个特点是 先到先执行。
- 当 有 default 执行
default。
当 select 的从句执行完成后, 整个 select 就退出了。
因此, 在服务端通常会配合 for 一起使用。
- 当 select 没有从句满足条件是, 就 阻塞 等待。
- 当 select 执行并退出后, 被
for捕获, 进入下一次循环。
func foreverSelect(ctx context.Context) {
for {
select {
case <-ctx.Done():
// 执行的代码
case <-time.After(10 * time.Second):
// 执行的代码
}
}
}
当需要自定义的服务保持一直运行不退出的时候, 可以使用 阻塞 特性。 (见过,没这样用过)
func foreverWaiting() {
// 自己实现了一个服务器
server.Run()
// 阻塞以保持服务器始终运行
select {}
}
目前就发现这么多用法, 如果不写并发服务的话, 估计一辈子用不到 select。 T.T