Go语言中的Select:深度解析与实战案例

这篇具有很好参考价值的文章主要介绍了Go语言中的Select:深度解析与实战案例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

select 是操作系统中的系统调用,我们以前在学校中学习操作系统课程或者在工作当中,肯定都使用过或者了解过 selectpoll 和 epoll 等函数构建 I/O 多路复用模型提升程序的性能。Go 语言的 select 与操作系统中的 select 很相似,今天这篇文章会深度解析 Go 语言 select 关键字。

在Go语言中,select语句用于处理多个通信操作,如通道操作。它允许我们等待多个操作完成,并根据条件执行相应的代码块。select语句在并发编程中非常有用,特别是当我们需要处理多个通道操作时。

语法相关

select语句的语法结构如下:

select {  
case <-channel1:  
    // 当channel1接收到数据时执行的代码块  
case <-channel2:  
    // 当channel2接收到数据时执行的代码块  
// ...  
default:  
    // 如果没有任何通道接收数据时执行的代码块  
}

select语句的执行

select语句会一直监听所有指定的通道,直到其中一个通道准备好就会执行相应的代码块。

如果多个通道都准备好了,则select语句会随机选择一个通道执行。

如果没有任何通道准备好,则会执行default分支。

select语句的注意事项

  • select语句只能用于通道操作,每个case语句必须是一个通道操作。
  • select语句会一直监听所有指定的通道,直到其中一个通道准备好就会执行相应的代码块。
  • 如果多个通道都准备好了,则select语句会随机选择一个通道执行。
  • 如果没有任何通道准备好,则会执行default分支(注意:如果没有default,程序则会陷入阻塞)。

深度解析

非阻塞与阻塞操作
  • 非阻塞操作:默认情况下,通道操作是阻塞的,即它会等待数据可用。如果要进行非阻塞操作,可以使用带有select的通道操作,例如select <-channel。非阻塞操作会立即返回,如果通道为空则继续执行下一个casedefault
  • 阻塞操作:当通道被阻塞时,它会一直等待直到有数据可用。这通常用于同步操作,确保发送和接收之间的正确匹配。阻塞操作可以用于确保数据的完整性和顺序。
发送与接收操作
  • 发送操作:除了通道接收操作外,还可以使用通道发送操作。发送操作可以在select语句中使用,但通常与接收操作一起使用,以确保发送和接收之间的同步。发送操作可以在case语句中使用,例如channel <- data
  • 接收操作:通道接收操作使用<-channel语法,用于从通道中读取数据。当通道接收到数据时,相应的case代码块将被执行。如果没有任何通道接收到数据,将执行default代码块或继续执行下一个case
超时与抢占
  • 超时:在使用select语句时,可以使用带有超时的通道操作来指定等待的时间限制。例如,可以使用带有超时的发送和接收操作,如下面代码。如果超过指定的时间没有数据可用,将执行相应的代码块或继续执行下一个case
select {
	case <-channel1:
		timeout := time.After(2 * time.Second)
		<-timeout
}
  • 抢占:在某些情况下,可能希望中断当前阻塞的通道操作并执行其他代码。Go语言的运行时支持抢占机制,可以用于中断阻塞的操作。当一个goroutine被抢占时,它会立即停止当前的操作并执行其他代码。这有助于避免死锁和饥饿问题。
死锁与饥饿问题
  • 死锁:在使用select语句时,需要注意死锁问题。死锁通常发生在多个通道之间相互等待数据时,导致所有通道都无法继续执行。为了解决死锁问题,可以使用带有优先级的通道或使用其他并发控制机制来确保正确的同步和通信。
  • 饥饿问题:饥饿问题发生在某些情况下,某些通道总是得不到执行的机会。为了避免饥饿问题,可以使用公平调度策略或限制每个通道的执行时间。此外,使用缓冲通道可以帮助平衡多个goroutine之间的通信和数据传输。

代码案例

下面是阻塞版的代码: 

package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan string) // 创建一个字符串类型的无缓冲通道ch1
	ch2 := make(chan string) // 创建一个字符串类型的无缓冲通道ch2

	go func() { // 启动一个新的goroutine来模拟异步任务并发送数据到ch1通道中
		time.Sleep(2 * time.Second) // 休眠2秒以模拟异步任务耗时的情况
		ch1 <- "Hello from ch1"     // 向ch1通道发送数据"Hello from ch1"
	}() // 结束匿名函数并启动goroutine

	go func() { // 启动一个新的goroutine来模拟异步任务并发送数据到ch2通道中
		time.Sleep(1 * time.Second) // 休眠1秒以模拟异步任务耗时的情况
		ch2 <- "Hello from ch2"     // 向ch2通道发送数据"Hello from ch2"
	}() // 结束匿名函数并启动goroutine

	select { // 使用select语句等待任意一个通道接收到数据
	case msg1 := <-ch1: // 当ch1通道接收到数据时,将数据赋值给msg1变量并执行该case分支的代码块
		fmt.Println("Received from ch1:", msg1) // 打印接收到的数据
	case msg2 := <-ch2: // 当ch2通道接收到数据时,将数据赋值给msg2变量并执行该case分支的代码块
		fmt.Println("Received from ch2:", msg2) // 打印接收到的数据
	}
}

这里是非阻塞版的代码

package main

import (
	"fmt"
	"time"
)

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

	go func() {
		for {
			select {
			case v := <-ch1:
				fmt.Println("Received", v, "from ch1")
			case v := <-ch2:
				fmt.Println("Received", v, "from ch2")
			case <-time.After(1 * time.Second):
				fmt.Println("Timeout")
			}
		}
	}()

	time.Sleep(2 * time.Second)

	ch1 <- 1
	ch2 <- 2

	time.Sleep(2 * time.Second)
}

这段代码与之前的代码相比,主要有以下几个改动:

  • select 语句使用了 time.After(1 * time.Second) 超时。这意味着,如果在 1 秒内没有通道有可用的数据,那么 select 语句会立即返回,并执行 Timeout 语句。
  • 即使在 ch1 和 ch2 都没有可用的数据时,select 语句也不会阻塞,而是会在 1 秒后返回,并执行 Timeout 语句。
  • 注意上面的select 语句没有 default 分支,但它仍然是非阻塞的。这是因为,time.After(1 * time.Second) 超时本身就相当于一个 default 分支。如果在 1 秒内没有通道有可用的数据,那么 select 语句会立即返回,并执行 Timeout 语句。因此,在使用超时实现非阻塞的 select 语句时,不需要加 default 分支。文章来源地址https://www.toymoban.com/news/detail-821828.html

到了这里,关于Go语言中的Select:深度解析与实战案例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 100天精通Golang(基础入门篇)——第23天:错误处理的艺术: Go语言实战指南

    🌷🍁 博主猫头虎🐅🐾 带您进入 Golang 语言的新世界✨✨🍁 🦄 博客首页 ——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍专栏》 🐾 学会IDEA常用操作,工作效率翻倍~💐 🌊 《100天精通

    2024年02月07日
    浏览(67)
  • go 语言实战入门案例之命令行排版词典

    文章和代码已经归档至【Github仓库:https://github.com/timerring/backend-tutorial 】或者公众号【AIShareLab】回复 go 也可获取。 先看一下用到的 API ,以彩云科技提供的在线翻译为例。请打开彩云翻译的网页,然后右键检查打开浏览器的开发者工具。 此时我们点一下翻译按钮,浏览器会

    2024年02月14日
    浏览(36)
  • 100天精通Golang(基础入门篇)——第5天: Go语言中的数据类型学习

    🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批

    2024年02月08日
    浏览(47)
  • Go 语言的实战案例 SOCKS5 代理 | 青训营

    Powered by: NEFU AB-IN GO语言工程实践课后作业:实现思路、代码以及路径记录 代理是指在计算机网络中,代理服务器充当客户端和目标服务器之间的中介。它接收来自客户端的请求,然后将请求转发给目标服务器,再将目标服务器的响应返回给客户端。 用途 : 匿名浏览 :Soc

    2024年02月10日
    浏览(44)
  • 掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)

    在Go语言中,范围(range)用于迭代数组、切片、映射、通道等数据结构的元素。范围的语法形式为 for range ,可以遍历集合中的每个元素,并在每次迭代中返回元素的索引(或键)和对应的值。 Go语言范围使用方法 使用范围语句的一般形式为: 其中, index 是元素的索引(或

    2024年04月17日
    浏览(46)
  • 深入解析 Go 语言中的 http.FileSystem

    本篇博文将深入研究 Go 语言中的 http.FileSystem 接口,这是在构建 Web 应用程序时至关重要的一部分。通过对 http.FileSystem 接口的深入探讨,我们将了解其基本原理、使用方法以及实际应用场景。 首先,我们将介绍 http.FileSystem 的基本概念和作用,以便读者对其有一个清晰的认识

    2024年03月19日
    浏览(38)
  • Go语言中的数组、切片和映射解析

    数组存放的是固定长度、相同类型的数据,而且这些存放的元素是连续的。 例如声明一个整形数组: 在类型名前加 [] 中括号,并设置好长度,大括号中的元素用于初始化数组,需要注意的是数组的长度不同,即属于不同的类型。 如果所有元素都被初始化的数组,声明时可以

    2024年02月09日
    浏览(46)
  • 【Golang星辰图】数据管理利器:Go编程语言中的数据库和搜索引擎综合指南

    Go编程语言是一种强大、类型安全且高效的编程语言,它在处理数据库和搜索引擎方面有着广泛的应用。本篇文章将详细介绍几个Go编程语言中常用的数据库和全文搜索引擎,包括Go-bleve、Go-pgx、Go-leveldb/leveldb、Go-xorm、Go-mysql-driver和Go-bbolt/bbolt。对于每个工具,我们将介绍其功

    2024年03月26日
    浏览(72)
  • 100天精通Golang(基础入门篇)——第15天:深入解析Go语言中函数的应用:从基础到进阶,助您精通函数编程!(进阶)

    🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批

    2024年02月12日
    浏览(68)
  • 深度学习实战应用:分享一些深度学习在实际问题中的应用案例和经验

    目录 一、引言 二、案例一:计算机视觉——图像分类

    2024年02月02日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包