Menu
CoddyTech

select 语句

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 62 课,共 107 课。

select 语句可以让 goroutine 同时等待多个通道操作。它类似于 switch 语句,但每个 case 都涉及通道发送或接收。当多个通道都准备就绪时,select 会随机选择一个。

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() { ch1 <- "from channel 1" }()
    go func() { ch2 <- "from channel 2" }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

如果不使用 select,从 ch1 接收数据时会一直阻塞,直到数据到达,这可能会错过 ch2 上的数据。使用 select 后,哪个通道先收到数据,就先处理哪个通道。

default 分支让 select 不会阻塞。如果没有就绪的通道,它会立即执行:

select {
case msg := <-messages:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message available")
}

一种常见模式是将 select 与使用 time.After 的超时机制结合起来。它会返回一个通道,并在指定的时间间隔后接收一个值:

select {
case result := <-ch:
    fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

这样可以避免通道操作可能永远无法完成时,你的程序一直等待。

challenge icon

挑战

简单

让我们构建一个服务监控器,使用 select 语句处理多个并发数据源。监控器会同时监听不同的服务通道,并在收到数据或没有可用更新时做出相应响应。

你将在两个文件中编写代码:

  • monitor.go:定义服务监控逻辑,并使用基于 select 的通道处理方式。

    创建一个 ServiceUpdate 结构体,包含 Name(字符串)和 Status(字符串)字段。

    实现一个 Monitor 函数,接收三个通道:primary chan ServiceUpdate、backup chan ServiceUpdate 和 done chan bool。此函数应使用 select 同时监听这三个通道:

    • 从 primary 接收时:返回格式化字符串 Primary: [Name] is [Status]
    • 从 backup 接收时:返回格式化字符串 Backup: [Name] is [Status]
    • 从 done 接收时:返回 Monitoring stopped

    实现一个 CheckStatus 函数,接收一个 updates chan ServiceUpdate,并使用带有 default 分支的 select 进行非阻塞检查:

    • 如果有可用更新:返回 Update available: [Name]
    • 如果没有可用更新(default 分支):返回 No updates pending
  • main.go:设置通道,并演示阻塞和非阻塞的 select 行为。

    从输入中读取一个模式,用来决定运行哪种情形:primary、backup、stop 或 check。

    对于 primary、backup 和 stop 模式:还要读取服务名称和状态。创建三个通道,启动一个 goroutine,根据模式向相应的通道发送数据,然后调用 Monitor 并打印其结果。

    对于 check 模式:读取服务名称和状态,以及一个标志(send 或 nosend),用于指示检查前是否发送数据。创建一个容量为 1 的缓冲通道。如果标志是 send,就在调用 CheckStatus 前向通道发送一条更新。打印 CheckStatus 的结果。

输入内容如下:

  • 第 1 行:模式(primary、backup、stop 或 check)
  • 第 2 行:服务名称
  • 第 3 行:服务状态
  • 第 4 行(仅用于 check 模式):send 或 nosend

例如,给定:

primary
Database
healthy

输出应为:

Primary: Database is healthy

再例如,给定:

check
Cache
active
nosend

输出应为:

No updates pending

亲手试试

package main

import (
	"fmt"
)

func main() {
	var mode string
	fmt.Scanln(&mode)

	var serviceName string
	fmt.Scanln(&serviceName)

	var serviceStatus string
	fmt.Scanln(&serviceStatus)

	// TODO:处理不同模式:"primary"、"backup"、"stop"、"check"
	
	// 对于 "primary"、"backup"、"stop" 模式:
	// 创建 3 个通道:primary、backup、done
	// 启动一个 goroutine,向对应的通道发送数据
	// 调用 Monitor 并打印结果
	
	// 对于 "check" 模式:
	// 读取额外的标志("send" 或 "nosend")
	// 创建一个容量为 1 的缓冲通道
	// 如果标志是 "send",就向通道发送更新
	// 调用 CheckStatus 并打印结果
	
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器