跳到主要内容

Go select语句

提示
  1. 选择通道执行: Go的select语句允许在多个通道之间选择一个执行,类似于switch语句,但用于通道。
  2. 单个准备好的通道: 如果多个通道中只有一个准备好,select会执行该通道;如果多个都准备好,则随机选择一个执行。
  3. 阻塞与默认情况处理: 当所有通道都不准备好时,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

在上面的例子中,我们创建了两个通道 numbermessage。这里,我们使用了 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 将随机执行其中一个通道。