go选择器select

select

select(选择器),可以同时等待多个通道操作。

将协程、通道和选择器结合,是Go的一个强大特性。

在Go语言中,select语句用于处理一个或多个channel操作,类似switch语句,但用于通信操作而不是切换操作。

select语句用于等待多个通信操作中的一个完成,然后执行相应的动作。

select语句使得goroutine可以等待多个channel操作,使得程序更具有并发性。

  • select语句的基础语法
select {
    case <- channel1:
        //当channel1可以读取时执行
    case data := <= channel2:
        //当channel2可以读取时执行此处代码,同时将读取的数据复制给data变量
    case channel3 <- value:
        //当channel3可以写入时执行此处的代码,将value写入channel3
    default:
        //当上述case语句都不满足时,执行此处的代码 
}

select语句中的每个case语句必须是一个channel的发送操作、接收操作或者是空操作(空操作表示什么都不做),select会阻塞,直到某个case语句可以执行,然后它会执行那个case,其他的被忽略,如果有多个case同时满足条件,会随机选择一个执行。

例子1

package main

import(
    "fmt"
    "time"
)

func main() {
    //定义channel
    channel1 := make(chan string)
    channel2 := make(chan string)
    channel3 := make(chan string)
    channel4 := make(chan string)
    channel5 := make(chan string)
    channel6 := make(chan string)
    channel7 := make(chan string)

    //起一个go向channel写入数据
    go func() {
        time.Sleep(2 * time.Second)
        channel1 <- "Hello from channel 1"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        channel2 <- "Hello from channel 2"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        channel3 <- "Hello from channel 3"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        channel4 <- "Hello from channel 4"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        channel5 <- "Hello from channel 5"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        channel6 <- "Hello from channel 6"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        channel7 <- "Hello from channel 7"
    }()

    select {
        case msg1 := <-channel1:
            fmt.Println(msg1)
        case msg2 := <-channel2:
            fmt.Println(msg2)
        case msg3 := <-channel3:
            fmt.Println(msg3)
        case msg4 := <-channel4:
            fmt.Println(msg4)
        case msg5 := <-channel5:
            fmt.Println(msg5)
        case msg6 := <-channel6:
            fmt.Println(msg6)
        case msg7 := <-channel7:
            fmt.Println(msg7)
    }
}

上述例子中,起了7个goroutine并发向channel写入数据。

使用select语句,程序会等待最早准备好的channel,并将其消息打印出来。这种方式可以确保程序不会因为等待一个channel的时间过长而被阻塞,能够充分利用并发性。

例子2

for + select 组合

参考:https://go.dev/tour/concurrency/5

  • 执行流程步骤
  1. 这是常见的并发模式,表示"不断监听多个channel事件"。
  2. 每一次循环,select会随机选择一个准备好的通道分支执行。
  3. 如果没有一个通道准备好,select会阻塞,直到有通道就绪。
  4. select 的每轮执行都是新的选择,它不会轮训,而是随机选择准备好的case。
  5. select + channel 是实现非阻塞控制流的关键

使用方式

for {
	select {
		// 多个通信分支
	}
}
package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}
  • 输出结果
0
1
1
2
3
5
8
13
21
34
quit

Reference

https://xargin.com/go-select/

https://gobyexample-cn.github.io/select

posted @ 2025-08-07 18:09  biby  阅读(11)  评论(0)    收藏  举报