GoLang - select

文章目录

Go 语言 select 语句

1、概述

  • select 是 Go 中的一个控制结构,类似于 switch 语句;
  • select 语句只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收;
  • select 语句会监听所有指定的通道上的操作:
    • 一旦其中一个通道准备好就会执行相应的代码块;
    • 如果多个通道都准备好,那么 select 语句会随机选择一个通道执行;
    • 如果所有通道都没有准备好,那么执行 default 块中的代码;
    • 如果所有通道都没有准备好,并且没有 default 块,那么 select 语句将会阻塞,直到至少一个通道准备好为止;(这意味着程序会一直等待,直到有一个通道可以进行操作)

2、语法

go 复制代码
select {
  case <- channel1:
    // 执行的代码
  case value := <- channel2:
    // 执行的代码
  case channel3 <- value:
    // 执行的代码

    // 你可以定义任意数量的 case

  default:
    // 所有通道都没有准备好,执行的代码
}
  • 每个 case 都必须是一个通道;
  • 所有 channel 表达式都会被求值;
  • 所有被发送的表达式都会被求值;
  • 如果任意摸个通道可以运行,它就执行,其他被忽略;
  • 如果有多个 case 都可以运行,select 会随机公平地选出一个执行,其他不会执行;
    • 否则,如果有 default 子句,则执行该语句;
    • 如果没有 default 子句,select 将阻塞,直到某个通道可以运行;
    • Go不会重新对 channel 或值进行求值;

3、简单实例

  • 没有 default 的 select;
go 复制代码
func SimpleDemoNoDefault() {
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		time.Sleep(1 * time.Second)
		c1 <- "channel one"
	}()

	go func() {
		time.Sleep(1 * time.Second)
		c2 <- "channel two"
	}()

	for i := 0; i < 3; i++ {
		select {
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}
}
  • 有 default 的 select:
go 复制代码
func SimpleDemoWithDefault() {
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		for {
			c1 <- "from 1"
		}
	}()

	go func() {
		for {
			c1 <- "from 2"
		}
	}()

	for {
		time.Sleep(1 * time.Second)
		select {
		case msg1 := <-c1:
			fmt.Println(msg1)
		case msg2 := <-c2:
			fmt.Println(msg2)
		default:
			fmt.Println("no message received")
		}
	}
}

4、实现原理

通过 select 语句,可以实现主线程和其他线程之间的互动;

实现原理

详情参考:https://www.jb51.net/article/259610.htm

  • GoLang实现select时,定义了一个数据结构表示每个case语句(包含defaultdefault实际上是一种特殊的case);
  • select执行过程可以看成一个函数,函数输入case数组,输出选中的case,然后程序流程转到选中的case块;

执行流程

创建select 注册case 执行select 释放select

  • 在默认情况下,select 在编译阶段经过如下过程的处理:
    • 将所有 case 转换成包含 channel 以及类型等信息的 scase 结构体;
    • 编译器调用运行时函数 selectgo 来执行 select 语句,这个函数会根据 scase 结构体数组中的各个 case 条件,选择其中一个 case 来执行,并返回被选择 scase 结构体的索引;
    • 如果当前 scase 是一个接收操作,函数会返回一个布尔值,表示接收操作是否成功;
    • 编译器会根据 scase 结构体数组中的各个 case 条件,生成一组 if 语句,每个 if 语句会判断当前的 case 是否是被选择的 case,如果是,则执行相应的操作;

scase 数据结构

源码包 src/runtime/select.go

go 复制代码
type scase struct {
	c    *hchan         // chan
	elem unsafe.Pointer // data element
}
  • c:表示通道,存储 case 所关联的通道,不包含 default;这也说明了一个case 语句只能操作一个 channel;
  • elem:表示数据类型,用于存储 case 所关联的数据元素;

判断某个 scase 属于什么操作

go 复制代码
select {
case x := <-ch:
    fmt.Println("接收到数据:", x)
case ch <- 10:
    fmt.Println("发送数据成功")
default:
    fmt.Println("没有进行任何操作")
}
  • <-ch:表示接收操作,将通道 ch 中的数据赋值给变量 x;
  • ch <- 10:表示发送操作,将数据 10 发送到通道 ch 中;
  • default:表示默认操作,当没有其他 case 可执行时,执行该操作;

5、应用场景

多通道读取

go 复制代码
func MultiChannelRead() {
	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		ch1 <- 1
	}()

	go func() {
		ch2 <- 2
	}()

	select {
	case data := <-ch1:
		fmt.Println("received data from ch1: ", data)
	case data := <-ch2:
		fmt.Println("receiver data from ch2: ", data)
	}
}

多通道写入

go 复制代码
func MultiChannelWrite() {
	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		ch1 <- 1
	}()

	go func() {
		ch2 <- 2
	}()

	select {
	case <-ch1:
		fmt.Println("Send data to ch1")
	case <-ch2:
		fmt.Println("Send data to ch2")
	}
}

超时控制

go 复制代码
func TimeoutControl() {
	ch := make(chan int)

	go func() {
		time.Sleep(2 * time.Second)
	}()

	select {
	case data := <-ch:
		fmt.Println("Received data: ", data)
	case <-time.After(1 * time.Second):
		fmt.Println("Timeout occurred")
	}
}
相关推荐
阿里云大数据AI技术19 小时前
Hologres AI Function 文本分类实战:从提示词设计到 KV-Cache 调优,全程 SQL 搞定
人工智能·sql
白帽小丑20 小时前
# 一次 MySQL DELETE 误操作的数据恢复尝试实录
数据库·mysql
Quincy_Freak1 天前
信创内网数据规范实践:银河麒麟下SQLite本地数据安全管理方案
数据库·sqlite·arm·数据库管理·大数据分析·银河麒麟·aarch64
开发小程序的之朴1 天前
认识安企CMS-安装安企CMS的环境要求
nginx·golang·系统架构
2601_962683891 天前
治理遗留系统中的“生肉 SQL”:一次用多模型协作优化慢查询的实战复盘
数据库·人工智能·sql
北冥you鱼1 天前
abigen 最佳实践:从入门到精通,高效生成 Go 语言合约绑定
开发语言·golang·区块链
酱学编程1 天前
【从零到一实现一个 AI Agent 框架 · 第四篇】04. 任务规划:拆解复杂目标 -
服务器·网络·数据库·人工智能
shushangyun_1 天前
2026智能采购商城系统选型指南:如何引领企业数字化采购升级
java·大数据·数据库·人工智能·机器学习
dexi.Chi 攻城狮1 天前
SQL层次查询语法
经验分享·笔记·sql
华山令狐虫1 天前
DBAPI AI 写 SQL:支持动态 SQL 与参数占位符,自然语言一键生成
数据库·人工智能·sql·dbapi