select
关键字用于处理同时来自多个通道的数据。它的基本工作原理是“随机选择”满足条件的分支去执行。如果没有分支满足条件(即所有通道都无法读/写),select
会阻塞,直到有分支满足条件。如果select
包含default
分支,当其他分支都不满足条件时,default
分支会被执行。
Go
的select
底层使用了一种名为scase
的结构体,表示一个select
分支,包含了通道和对应的操作类型(发送或者接收)。同时,它还会使用一个名为hchan
的结构体来表示通道的内部结构。
以下是select
的一些重要的特性:
- 公平性:在
Go
语言中,select
语言会随机选择一个可以运行的case执行,保证了每一个case都有公平的机会被执行,避免了饥饿问题。 - 非阻塞:如果
select
中所有的case都无法运行,而且存在default
分支,那么select
就不会被阻塞,而是执行default
分支。 - 可用于时间操作:
select
经常与time.After
,time.Tick
等函数一起使用,用于实现超时操作或者定时操作。 - 可用于退出操作:
select
经常和context
一起使用,当收到context
的取消信号时,可以安全地退出协程。
package mainimport ("fmt""time"
)func selectExample(c1, c2 chan int, quit chan bool) {for {select {case v := <-c1:fmt.Println("Received from c1:", v)case v := <-c2:fmt.Println("Received from c2:", v)case <-quit:fmt.Println("Quit signal received. Exiting.")returndefault:fmt.Println("No data received.")time.Sleep(1 * time.Second) // 添加延迟以避免过度占用CPU}}
}func main() {// 创建通道c1 := make(chan int)c2 := make(chan int)quit := make(chan bool)// 启动selectExample函数作为goroutinego selectExample(c1, c2, quit)// 启动发送数据到c1的goroutinego func() {for i := 0; i < 5; i++ {c1 <- itime.Sleep(1 * time.Second)}}()// 启动发送数据到c2的goroutinego func() {for i := 0; i < 5; i++ {c2 <- i + 10time.Sleep(2 * time.Second)}}()// 从主goroutine发送退出信号time.Sleep(10 * time.Second) // 等待足够时间以查看输出quit <- truefmt.Println("Sent quit signal and waiting to exit.")
}
最后给大家推荐一个LinuxC/C++高级架构系统教程的学习资源与课程,可以帮助你有方向、更细致地学习C/C++后端开发,具体内容请见 https://xxetb.xetslk.com/s/1o04uB