《Shell 转 Go》
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

05. 并发选择语句 (select)

select 语句在 Shell 中没有, 但是在 Go 中却是 并发 的根基。

select

在语法上面, 和 switch 相似

  1. 使用 case 作为分支入口。
  2. 使用 default 作为默认语句。

不同的是,

  1. 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:
		// 所有通道都没有准备好,执行的代码
	}
}
  1. 每个 case 语句都必须是一个通道(channel)。 常见的通道包括以下:
  • ctx.Done
  • time.After
  • chan 定义的通道
  1. 所有 通达都会 被求值。 无论 进入或发送
  2. 如果在进入 select 的时候, 已经有 多个分支 的通道 就绪, 则 随机 选择一个。
  3. 如果任意分支执行了, 其他分支就不执行了。
  4. 关于 default 分支, 其他分支没准备好是
  • 没有 default阻塞。 由于 阻塞 的特性, 所以有一个特点是 先到先执行
  • 有 default 执行 default

当 select 的从句执行完成后, 整个 select 就退出了。

一直选择

因此, 在服务端通常会配合 for 一起使用。

  1. 当 select 没有从句满足条件是, 就 阻塞 等待。
  2. 当 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