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