首页 > 其他分享 >select 条件语句【GO 基础】

select 条件语句【GO 基础】

时间:2024-02-26 17:01:10浏览次数:26  
标签:语句 case fmt chan func GO main select

〇、select 简介

select 语句类似于 switch 语句,但是 select 会随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。

select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。默认的子句 default 应该总是可运行的。

操作系统中实现 IO 多路复用的命令 select、poll、epoll,主要通过起一个线程,来监听并处理多个文件描述符代表的 TCP 链接,用来提高处理网络读写请求的效率。而 Go 语言的 select 命令,是用来起一个 goroutine 协程监听多个 Channel(代表多个 goroutine)的读写事件,提高从多个 Channel 获取信息的效率。二者具体目标和实现不同,但本质思想都是相同的

一、select 测试

1.1 当没有 case 可运行时

由于 Go 自带死锁检测机制,当发现当前协程(goroutine )没有机会被唤醒时,则会发生 panic

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select]:
main.main()
        E:/test/main.go:8 +0x87

情况一:select 内容为空:

package main
func main() {
	select {}
}

此时 goroutine 默认永远阻塞。

情况二:select 内容不为空,但没有可运行的 case:

package main

import "fmt"

func main() {
	ch1 := make(chan int, 1)
	ch2 := make(chan int)
	select {
	case <-ch1:
		// 从有缓冲chan中读取数据,由于缓冲区没有数据且没有发送者,该分支会阻塞
		fmt.Println("Received from ch")
	case i := <-ch2:
		// 从无缓冲chan中读取数据,由于没有发送者,该分支会阻塞
		fmt.Printf("i is: %d", i)
	}
}

1.2 select 的默认运行项 default

当没有其他可运行的 case 时,执行 default 分支:

package main

import "fmt"

func main() {
	ch1 := make(chan int, 1)
	select {
	case <-ch1:
		// 从有缓冲 chan 中读取数据,由于缓冲区没有数据且没有发送者,该分支会阻塞
		fmt.Println("Received from ch")
	default:
		fmt.Println("this is default")
	}
}

输出:

1.3 同时存在多个可执行的分支时,随机执行其中一个

如下三个分支均满足执行条件:

package main

import "fmt"

func main() {
	ch := make(chan int, 1)
	ch <- 10
	select {
	case val := <-ch:
		fmt.Println("Received from ch1, val =", val)
	case val := <-ch:
		fmt.Println("Received from ch2, val =", val)
	case val := <-ch:
		fmt.Println("Received from ch3, val =", val)
	default:
		fmt.Println("Run in default")
	}
}

如下图连续运行了四次,可以看出执行的 case 并不固定:

二、几个典型用法

2.1 超时判断

如下代码,使用全局 resChan 来接受 response,如果时间超过 3s,resChan 中还没有数据返回,则第二条 case 将执行。

package main

import (
	"fmt"
	"time"
)

func main() {
	test()
}

var resChan = make(chan int)

func test() {
	select {
	case data := <-resChan:
		fmt.Println("data:", data)
	case <-time.After(time.Second * 3): // 配置超时时间 3 秒钟
		fmt.Println("request time out")
	}
}

结果输出:

2.2 根据信号退出运行

如下代码,在协程中操作关闭通道,主线程监控到后退出:

package main

import (
	"fmt"
	"time"
)

var shouldQuit = make(chan struct{})

func main() {
	fmt.Println("main...")
	go quit() // 另开一个协程 goroutine

	select {
	case <-shouldQuit:
		fmt.Println("shouldQuit...")
		return
	case <-time.After(time.Second * 2): // 超时时调用
		fmt.Println("request time out")
	}
}

func quit() {
	time.Sleep(1 * time.Second)
	close(shouldQuit) // close 函数用来关闭通道,表示没有更多的值将被发送到该通道
}

当协程中 time.Sleep() 配置小于 2s,就会执行 select 中的第一个 case,超过 2s 时,就会执行第二个 case 提示超时。

2.3 判断 channel 是否阻塞

判断是否阻塞,实际上就是加上 default,当其他 case 都获取不到值时,通过 default 的操作,来知道 channel 是否阻塞:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int, 1)

	// // 向 channel 发送一个值
	// ch <- 1
	// fmt.Println("向 channel 发送了一个值:1")

	// 使用 select 语句判断 channel 是否阻塞
	select {
	case v := <-ch:
		fmt.Println("从 channel 中读取到值:", v)
	default:
		fmt.Println("channel 阻塞")
	}

	fmt.Println("模拟阻塞等待。。")
	// 模拟阻塞等待
	time.Sleep(2 * time.Second)
	fmt.Println("模拟阻塞等待。。2s")
}

参考:http://www.topgoer.com/%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6/%E6%9D%A1%E4%BB%B6%E8%AF%AD%E5%8F%A5select.html

三、select 并发编程

select 同时监控多个 channel,直到其中一个 channel 准备好:

package main

import (
	"fmt"
	"time"
)

func test1(ch chan string) {
	time.Sleep(time.Second * 5)
	ch <- "test1"
}
func test2(ch chan string) {
	time.Sleep(time.Second * 2)
	ch <- "test2"
}

func main() {
	// 2个管道
	output1 := make(chan string)
	output2 := make(chan string)
	// 跑2个子协程,写数据
	go test1(output1)
	go test2(output2)
	// 用select监控
	select {
	case s1 := <-output1:
		fmt.Println("s1 =", s1)
	case s2 := <-output2:
		fmt.Println("s2 =", s2)
	}
}

如下代码,判断管道是否存满:(设置写入比读取快)

package main

import (
	"fmt"
	"time"
)

// 判断管道有没有存满
func main() {
	// 创建管道
	output1 := make(chan string, 2)
	// 子协程写数据
	go write(output1)
	// 取数据
	for s := range output1 {
		fmt.Println("res:", s)
		time.Sleep(time.Second) // 间隔 1s 读取
	}
}

func write(ch chan string) {
	for {
		select {
		// 尝试写数据
		case ch <- "hello":
			fmt.Println("write hello")
		default:
			fmt.Println("channel full")
		}
		time.Sleep(time.Millisecond * 500) // 间隔 0.5s 写入
	}
}

参考: http://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/select.htm

若想了解 select 底层原理可以参考:https://cloud.tencent.com/developer/article/2205909

标签:语句,case,fmt,chan,func,GO,main,select
From: https://www.cnblogs.com/hnzhengfy/p/Go_select.html

相关文章

  • golang dlv程序调试
    安装dlv执行goinstall安装dlvgoinstallgithub.com/go-delve/delve/cmd/dlv@latest执行dlvversion查看是否安装成功$dlvversionDelveDebuggerVersion:1.22.0Build:$Id:61ecdbbe1b574f0dd7d7bad8b6a5d564cce981e9$断点调试dlv有以下三种方式进行断点调试:......
  • [Go Unit testing] Unit testing Go program
    Code:config.gopackageprojectorimport( "fmt" "os" "path")typeOperation=intconst( PrintOperation=iota Add Remove)typeConfigstruct{ Args[]string OperationOperation Configstring Pwdstring}f......
  • select/poll/epoll
    1、select的实现(fs/select.c)主要涉及三个函数sys_select()àcore_sys_select()àdo_select() 每次调用select,都会将用户态的fd拷贝至内核态do_select()会1)循环遍历每一个fd,调用对应的驱动的poll函数,poll函数会:1)将用户进程插入到驱动的等待队列中2)返回mask告知就绪f......
  • 基础知识-GO语言部分
    (240226)资料参考Go语言教程|runoobgolang的类型推断|jb51Golang-100-Days|Github|rubyhanGo语言基础之Context详解|zhihu|程序员祝融基础基本语法Go程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号。fmt.Println("Hello,World!")6个标记......
  • JavaSE的第八步 —— 循环语句
    一、循环循环在Java中主要是依靠两个关键字进行 一个是for关键字有关的,另一个是while关键字有关的循环二、for循环for(初始化条件;判断条件语句;迭代因子){语句块};在for循环执行的时候,首先需要执行第一个分号之前的语句,对判断条件进行初始化,之后对判断条件进行比较,如果判断为......
  • Python Django适配dm8(达梦)数据库
    官方文档https://eco.dameng.com/document/dm/zh-cn/start/python-development.htmlDjango适配达梦https://blog.csdn.net/qq_35349982/article/details/132165581https://blog.csdn.net/weixin_61894388/article/details/126330168项目适配达梦升级或安装依赖Django==3......
  • go psutil获取磁盘信息
    示例代码://通过psutil获取所有分区的信息//重要字段:/*fmt.Printf("Device:%s\n",partition.Device)fmt.Printf("Mountpoint:%s\n",partition.Mountpoint)fmt.Printf("Filesystemtype:%s\n",partition.Fstype)fmt.Printf(......
  • 这3款免费最佳Google翻译替代品,完美解决谷歌翻译国内用不了的尴尬
    前段时间Google谷歌翻译停止了中国区服务,谷歌翻译退出中国已经成为历史事件,导致Chrome谷歌浏览器无法翻译网页,使得很多小伙伴办公和学习都遇到阻碍,目前搜索谷歌翻译的网址无法正常访问,甚至连谷歌翻译的手机APP也无法使用。谷歌在线翻译突然撤出中国,这猝不及防的操作瞬间让很......
  • make集成go语言项目
    参考下面这个makefile.PHONY:builddebugdlvcleantoollinthelpall:buildbuild: @gobuild-v.debug: gobuild-gcflags"all=-N-l"-v-oapp.dlv: dlv--listen=:2345--headless=true--api-version=2--accept-multiclientexec./apptool:......
  • go语言和bash中处理csv
    golang中处理csv标准库中csv文件的一些内容varfileio.Readerreader:=csv.NewReader(file)gocsv库他有以下特点:简单的api来将csv内容解析成go结构体自定义解析特定类型的函数;自定义csv的reader和writer基本使用:下面的代码可以将csv文本的内容解析到切片中;pa......