Go select语句
- 选择通道执行: Go的
select
语句允许在多个通道之间选择一个执行,类似于switch语句,但用于通道。 - 单个准备好的通道: 如果多个通道中只有一个准备好,
select
会执行该通道;如果多个都准备好,则随机选择一个执行。 - 阻塞与默认情况处理: 当所有通道都不准备好时,
select
会阻塞,直到至少一个通道准备好。可以通过default
案例处理无通道准备好的情况,避免程序长时间阻塞。
Go 的 select 语句允许我们在多个备选项中执行一个通道。
在了解 select 之前,请确保你已经理解了 Go 通道。
Select 语句的语法
select {
case 第一个通道:
case 第二个通道:
case 第三个通道:
}
这里,select 的每个 case 代表一个独立的通道。根据通道操作的可用性,select 语句执行一个通道。
注意:select case 的语法看起来类似于 Go 中的 Switch Case。就像 switch case 一样,select 只执行其中一个 case。
示例:Golang select 语句
package main
import "fmt"
func main() {
// 创建两个通道
number := make(chan int)
message := make(chan string)
// 使用 goroutine 调用函数
go channelNumber(number)
go channelMessage(message)
// 选择并执行一个通道
select {
case firstChannel := <- number:
fmt.Println("通道数据:", firstChannel)
case secondChannel := <- message:
fmt.Println("通道数据:", secondChannel)
}
}
// goroutine 向通道发送整数数据
func channelNumber(number chan int) {
number <- 15
}
// goroutine 向通道发送字符串数据
func channelMessage(message chan string) {
message <- "学习 Go select"
}
输出
通道数据: 学习 Go select
在上面的例子中,我们创建了两个通道 number
和 message
。这里,我们使用了 goroutines:
channelNumber()
向 number 通道发送数据channelMessage()
向 message 通道发送数据
程序包含两个不同的通道,所以我们使用 select 语句在两个通道中选择并执行一个。
select {
case firstChannel := <- number:
fmt.Println("通道数据:", firstChannel)
case secondChannel := <- message:
fmt.Println("通道数据:", secondChannel)
}
这里,case firstChannel
从 number 通道获取值并打印。同样地,case secondChannel
从 message 通道获取值并打印。
当你运行这个程序时,你可能会得到不同的输出。在我们的例子中,两个通道都准备好执行,所以 select 语句随机执行一个通道。
Go select 与一个通道准备好执行
我们知道,当多个通道准备好执行时,select 语句随机执行一个通道。
然而,如果只有一个通道准备好执行,它会执行那个通道。例如,
package main
import (
"fmt"
"time"
)
func main() {
// 创建通道
number := make(chan int)
message := make(chan string)
// 使用 goroutine 调用函数
go channelNumber(number)
go channelMessage(message)
// 选择并执行一个通道
select {
case firstChannel := <-number:
fmt.Println("通道数据:", firstChannel)
case secondChannel := <-message:
fmt.Println("通道数据:", secondChannel)
}
}
// goroutine 向通道发送数据
func channelNumber(number chan int) {
number <- 15
}
// goroutine 向通道发送数据
func channelMessage(message chan string) {
// 使进程睡眠 2 秒
time.Sleep(2 * time.Second)
message <- "学习 Go Select"
}
输出
通道数据: 15
在上面的例子中,我们创建了两个 goroutines,
channelNumber()
- 向 number 通道发送数据channelMessage()
- 向 message 通道发送数据
在 channelMessage()
goroutine 中,我们使用了 time.Sleep()
方法使 message 通道在执行时不可用。
现在,在前2秒,只有 number 通道准备好执行。这就是为什么 select 语句执行 case firstChannel
(number 通道)。
Go select 用于阻塞通道
如果通道没有准备好执行,select 语句将阻塞所有通
道。假设,在我们之前的例子中,如果 number 和 message 通道都没有准备好执行,select 将阻塞这两个通道一定时间,直到其中一个可用于执行。
让我们看一个例子。
package main
import (
"fmt"
"time"
)
func main() {
// 创建通道
number := make(chan int)
message := make(chan string)
// 使用 goroutine 调用函数
go channelNumber(number)
go channelMessage(message)
// 选择并执行一个通道
select {
case firstChannel := <-number:
fmt.Println("通道数据:", firstChannel)
case secondChannel := <-message:
fmt.Println("通道数据:", secondChannel)
}
}
// goroutine 向通道发送数据
func channelNumber(number chan int) {
// 使进程睡眠 2 秒
time.Sleep(2 * time.Second)
number <- 15
}
// goroutine 向通道发送数据
func channelMessage(message chan string) {
// 使进程睡眠 2 秒
time.Sleep(2 * time.Second)
message <- "学习 Go Select"
}
输出
通道数据: 学习 Go Select
在上面的例子中,我们使用了 time.Sleep()
方法使两个通道在执行时都不可用 2 秒。
现在,select 语句将在前2秒阻塞这两个通道。这就是为什么我们在2秒内没有任何输出。
然后,在2秒后,因为两个通道都将可用,它随机执行其中一个通道。
Golang select 带有 default case
当没有通道准备好时,select 将阻塞程序。然而,最好是显示一些消息,而不是阻塞直到通道准备好。
为此,我们使用 default
case,如果没有通道准备好执行,就会执行它。例如,
package main
import (
"fmt"
"time"
)
func main() {
// 创建通道
number := make(chan int)
message := make(chan string)
// 使用 goroutine 调用函数
go channelNumber(number)
go channelMessage(message)
// 选择并执行一个通道
select {
case firstChannel := <-number:
fmt.Println("通道数据:", firstChannel)
case secondChannel := <-message:
fmt.Println("通道数据:", secondChannel)
// default case
default:
fmt.Println("等待!通道尚未准备好执行")
}
}
// goroutine 向通道发送数据
func channelNumber(number chan int) {
// 使进程睡眠 2 秒
time.Sleep(2 * time.Second)
number <- 15
}
// goroutine 向通道发送数据
func channelMessage(message chan string) {
// 使进程睡眠 2 秒
time.Sleep(2 * time.Second)
message <- "学习 Go Select"
}
输出
等待!通道尚未准备好执行
在上面的例子中,我们使用了带有 select 的 default
case,如果两个通道都没有准备好,它会打印 "等待!通道尚未准备好执行"
。
由于两个通道都睡眠了2秒,它们在第一次执行时都不会准备好。这就是为什么会执行 default
case 的语句。
在2秒后,两个通道都将准备好执行,select 将随机执行其中一个通道。