【go】select 语句case的随机性

发布于:2024-12-05 ⋅ 阅读:(129) ⋅ 点赞:(0)

在 Go 的 select 语句中,case 的匹配是随机的,并非按照顺序逐一匹配。以下是具体的机制和行为解释:


1. select 匹配机制

  • select 会同时监听所有 case 分支中的通道操作。
  • 如果多个 case 分支的条件同时满足(例如通道可读或可写):
    • Go 会随机选择一个满足条件的 case 来执行。
    • 这保证了 select 语句的非确定性,可以避免偏向某些通道。
  • 如果没有任何 case 条件满足:
    • 如果存在 default 分支,则执行 default 分支。
    • 如果没有 default 分支,select 会阻塞,直到某个 case 满足条件。

2. 示例代码

随机匹配的行为
package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	// 启动两个 Goroutine 向通道发送数据
	go func() { ch1 <- "Message from ch1" }()
	go func() { ch2 <- "Message from ch2" }()

	// 使用 select 监听通道
	select {
	case msg := <-ch1:
		fmt.Println(msg)
	case msg := <-ch2:
		fmt.Println(msg)
	}
}

可能输出
  • Message from ch1
  • Message from ch2
  • 输出是随机的,取决于哪个通道先被 Go 运行时选中。

3. 顺序匹配的假象

Go 的 select 本身没有顺序匹配,但如果程序的逻辑导致某些通道更早满足条件,会显得像是按顺序执行。例如:

 
 
 

package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		time.Sleep(1 * time.Second)
		ch1 <- "Message from ch1"
	}()

	go func() {
		time.Sleep(2 * time.Second)
		ch2 <- "Message from ch2"
	}()

	select {
	case msg := <-ch1:
		fmt.Println(msg)
	case msg := <-ch2:
		fmt.Println(msg)
	}
}

输出
  • Message from ch1
  • 因为 ch1 会在 1 秒后准备好,而 ch2 需要 2 秒,所以 ch1case 会更早满足条件。

4. 总结

特性 说明
匹配机制 多个 case 同时满足时,随机选择一个执行。
单个满足 如果只有一个 case 满足条件,则直接执行该分支。
无匹配 如果没有满足条件的 case 且无 defaultselect 阻塞。
default 如果存在 default,且无 case 满足条件,则立即执行 default
顺序执行 不是按代码顺序匹配,但某些条件可能使某个通道更早满足条件,看似按顺序。

通过随机匹配和阻塞机制,select 为 Go 并发提供了灵活且公平的通道操作能力。