Go语言并发之Select多路选择操作符

news2025/2/1 15:53:49

1、Go语言并发之Select多路选择操作符

select 是类 UNIX 系统提供的一个多路复用系统 API,Go 语言借用多路复用的概念,提供了 select 关键字,用

于多路监听多个通道。当监听的通道没有状态是可读或可写的,select 是阻塞的;只要监听的通道中有一个状态

是可读或可写,则 select 就不会阻寒,而是进入处理就绪通道的分支流程。如果监听的通道有多个口读或口写

的状态,则 select 随利选取一个处理。

package main

func main() {
	ch := make(chan int, 1)
	go func(chan int) {
		for {
			select {
			// 0或者1写入是随机的
			case ch <- 0:
			case ch <- 1:
			}
		}
	}(ch)
	for i := 0; i < 10; i++ {
		println(<-ch)
	}

}
# 程序结果
1
1
1
1
0
0
0
0
1
1

1.1 多路选择操作符 select

在golang语言中,select 语句就是用来监听和channel有关的IO操作,当IO操作发生时,触发相应的case动作。

有了select语句,可以实现 main 主线程与 goroutine 线程之间的互动。

select使用时类似 switch-case 的用法,适用于处理多通道的场景,会通过类似 are-you-ready-polling 的机制

来工作。

select {
    case <-ch1 :     // 检测有没有数据可读
        // 一旦成功读取到数据,则进行该case处理语句
    case ch2 <- 1 :  // 检测有没有数据可写
        // 一旦成功向ch2写入数据,则进行该case处理语句
    default:
        // 如果以上都没有符合条件,那么进入default处理流程
}
  • select 语句只能用于 channel 信道的IO操作,每个 case 都必须是一个信道。

  • 如果不设置 default 条件,当没有IO操作发生时,select 语句就会一直阻塞。

  • 如果有一个或多个IO操作发生时,Go运行时会随机选择一个 case 执行,但此时将无法保证执行顺序。

  • 对于 case 语句,如果存在信道值为 nil 的读写操作,则该分支将被忽略,可以理解为相当于从select语句中

    删除了这个case;

  • 对于空的 select 语句,会引起死锁;

  • 对于在 for中的select语句,不能添加 default,否则会引起cpu占用过高的问题;

  • 随机性:多个 case 之间并非顺序的,遵循「先到先执行,同时到则随机执行」的原则。

  • 一次性:和 switch-case 一样,select-case也只会执行一次,如果需要多次处理,需要在外层套一个循环。

  • default 不会阻塞,会一直执行,当与 for 循环组合使用时可能出现死循环。

1.2 阻塞与非阻塞 select

select 默认是阻塞的,当没有 case 处于激活状态时,会一直阻塞住,极端的甚至可以这样用:

package main

func main() {
	select {
	// 啥也不干,一直阻塞住
	}
}

执行后,引发死锁,打印如下:

# 输出
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:

通过增加 default,可以实现非阻塞的 select:

select {
    case x, ok := <-ch1:
        ...
    case ch2 <- y:
        ...
    default:
        fmt.Println("default")

}

1.3 多 case 与 default 执行的顺序

整体流程如图所示:
在这里插入图片描述

1.4 多个IO操作发生时,case语句是随机执行的

package main

import "fmt"

func main() {
	// 创建一个长度带缓冲的整型通道
	ch1 := make(chan int, 1)
	// 向通道中写入数据
	ch1 <- 1
	ch2 := make(chan int, 1)
	ch2 <- 2
	select {
	case <-ch1:
		fmt.Println("ch1 read")
	case <-ch2:
		fmt.Println("ch2 read")
	}
}

多次执行后,会随机打印 ch1 read 或 ch2 read。

1.5 for中的select 引起CPU资源消耗过高

package main

import (
	"fmt"
	"time"
)

func main() {
	quit := make(chan bool)
	go func() {
		for {
			select {
			case <-quit:
				fmt.Println("quit")
				// 使用 return 就会退出整个goroutine线程;如果使用 break,程序仍然在for循环中执行
				return
			default:
				fmt.Println("default")
			}
		}
	}()

	time.Sleep(3 * time.Second)
	quit <- true // 主线程在3秒后,向quit信道写入数据

	time.Sleep(2 * time.Second)
	fmt.Println("main")
}
# 程序结果
default
default
default
default
default
default
default
......
......
default
default
default
default
default
quit
main

在 for{} 的 select 语句中使用了 default 后,线程就会无限执行 default 条件,直到 quit 信道中读到数据,否

则会一直在一个死循环中运行,从而导致占满整个CPU资源。

在 for{} 的 select 语句中,不建议使用 default 条件。

1.6 select语句的实际应用

(1)、实现 main主线程与 goroutine线程之间的交互、通信

package main

import (
	"bufio"
	"fmt"
	"os"
)

// 通过控制台输入"bye",来控制main函数结束运行
func main() {
	quit := make(chan bool)
	ch := make(chan string)
	go func() {
		for {
			select {
			case name := <-ch:
				fmt.Printf("from main msg: [%v]\n", name)
				if name == "bye" {
					quit <- true
				} else {
					quit <- false
				}
			}
		}
	}()
	for {
		// 控制台输入
		fmt.Print("please input string: ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		ch <- scanner.Text()
		isOver := <-quit
		if isOver {
			break
		}
	}
	fmt.Println("main over")
}
# 输出
please input string: from main msg: [ttttt]
please input string: from main msg: [qqqq]
please input string: from main msg: [wwww]
please input string: from main msg: [bye]
main over

(2)、超时实现

package main

import (
	"fmt"
	"time"
)

func main() {
	quit := make(chan bool)
	ch := make(chan int)
	go func() {
		for {
			select {
			case num := <-ch:
				fmt.Println("num = ", num)
			case <-time.After(5 * time.Second):
				fmt.Println("超时")
				quit <- true
			}
		}
	}()
	for i := 0; i < 2; i++ {
		ch <- i
		time.Sleep(time.Second)
	}
	<-quit // 等待超时后, 结束 main主线程
	fmt.Println("程序结束")
}
# 输出结果
num =  0
num =  1
超时
程序结束

1.7 select使用的区别

package main

import (
	"fmt"
	"time"
)

func server1(ch chan string) {
	time.Sleep(6 * time.Second)
	ch <- "from server1"
}

func server2(ch chan string) {
	time.Sleep(3 * time.Second)
	ch <- "from server2"
}

func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	s1 := <-output1
	fmt.Println(s1)
	s2 := <-output2
	fmt.Println(s2)
}
# 程序结果
from server1
from server2
package main

import (
	"fmt"
	"time"
)

func server1(ch chan string) {
	time.Sleep(6 * time.Second)
	ch <- "from server1"
}

func server2(ch chan string) {
	time.Sleep(3 * time.Second)
	ch <- "from server2"
}

func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	select {
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
	}
}
# 程序结果
from server2
package main

import "time"

import (
	"fmt"
)

// select 管道参数并行

func server1(ch chan string) {
	time.Sleep(time.Second * 6)
	ch <- "response from server1"
}

func server2(ch chan string) {
	time.Sleep(time.Second * 3)
	ch <- "response from server2"
}

func main() {
	output1 := make(chan string)
	output2 := make(chan string)

	go server1(output1)
	go server2(output2)

	// 管道同时ready,select随机执行
	// time.Sleep(time.Second)
	select {
	case s1 := <-output1:
		fmt.Println("s1:", s1)
	case s2 := <-output2:
		fmt.Println("s2:", s2)
	default:
		fmt.Println("run default")
	}
}
# 程序结果
run default
package main

import (
	"fmt"
	"time"
)

func server1(ch chan string) {
	ch <- "from server1"
}

func server2(ch chan string) {
	ch <- "from server2"
}

func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	time.Sleep(1 * time.Second)
	select {
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
	}
}
# 输出
from server2 和 from server1 随机交替

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631293.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NoSQLBooster 8.0.11 for MongoDB

MongoDB最智能的IDE。 NoSQLBooster 是适用于 MongoDB Server 3.6-6.0 的跨平台 GUI 工具&#xff0c;它提供了内置的 MongoDB 脚本调试器、全面的服务器监控工具、链接流畅查询、SQL 查询、查询代码生成器、任务调度、ES2020 支持和高级 IntelliSense 体验。 嵌入式MongoDB S…

YUM源安装,在线YUM,本地YUM

YUM源 一、定义 YUM&#xff08;全称为 Yellow dog Updater, Modified&#xff09;是一个在 Fedora 和 RedHat 以及 CentOS 中的 Shell 前端软件包管理器。基于RPM包管理&#xff0c;能够从指定的服务器自动下载RPM包并且安装&#xff0c;**可以自动处理依赖性关系&…

【ChatGPT散文篇】ChatGPT-清华大学的讲座要点

刚听了清华大学关于ChatGPT的讲座&#xff0c;总结几个要点: 1、AI有可能成为地球上最初的硅基生命。 2、AI已迭代至博士研究生的智力水平。 3、以后将不存在学科和专业的概念&#xff0c;也不存在文科理科的区分&#xff0c;将是学科大融合。因为一个文科生哪怕不懂代码&a…

【Elasticsearch】结合Postman/ApiPost 快速入门

相比于MySQL那种关系型数据库&#xff0c;是采用正排索引&#xff0c;也就是根据主键去找其对应的行数据。而Elasticsearch这个NoSQL&#xff0c;是采用倒排索引&#xff0c;根据输入的某值返回其它整行的数据。而Elaticsearch是采用RESTful风格的数据库访问&#xff0c;以下就…

Docker 概述与命令操作

一、Docker 概述 1、Docker的概念 • Docker是一个开源的应用容器引擎&#xff0c;基于go语言开发并遵循了apache2.0协议开源 • Docker是在Linux容器里运行应用的开源工具&#xff0c;是一种轻量级的“虚拟机” • Docker 的容器技术可以在一台主机上轻松为任何应用创建一…

IDEA 集成Maven

一. 下载IDEA编辑器 下载地址&#xff1a;Download IntelliJ IDEA – The Leading Java and Kotlin IDE 选中免费开源的社区版本 创建桌面图标和添加安装路径到系统变量&#xff0c;其他的默认安装即可。 二. 配置Maven环境 分为局部配置和全局配置 1. 局部配置maven环境 …

编译tolua——4、更新luaJit

目录 1、编译工具和环境说明 2、基础编译tolua 3、以pbc为例子&#xff0c;添加第三方库 4、更新luaJit 大家好&#xff0c;我是阿赵。 用tolua做项目的朋友们&#xff0c;在这两年肯定会遇到过一个问题&#xff0c;就是现在的安卓应用市场比如谷歌市场&#xff0c;要求上传的…

Linux知识点 -- 进程控制(一)

Linux知识点 – 进程控制&#xff08;一&#xff09; 文章目录 Linux知识点 -- 进程控制&#xff08;一&#xff09;一、进程创建1.fork函数2.写时拷贝2.fork常规用法3.fork调用失败的原因 二、进程终止1.进程终止时操作系统的行动2.进程终止的常见方式3.用代码终止一个进程 三…

yolo-v3看不懂?手撕代码逐行讲解,附带网盘完整代码实现

目录 一&#xff1a;读取数据 二&#xff1a;初始化模型 Route 层用于将来自不同层的特征图进行连接或拼接。 Shortcut 层用于执行残差连接&#xff0c;将前一层的特征图与当前层的特征图相加。 最重要的一层 yolo层&#xff1a; 三&#xff1a;初始化完所有有网络层后&…

从零开始,5分钟轻松实现Spring Boot与RabbitMQ的无缝集成

&#x1f30f; 环境 docker v4.16.2springboot 2.7.0RabbitMQ 3.9.1 rabbitmq_delayed_message_exchange 3.9.0 ps&#xff1a;代码地址 gitee &#x1fa9c; 服务架构 使用maven多模块&#xff0c;将生产者、消费者分别以springboot项目启动&#xff0c;两者通过RabbitMQ…

【Spark基础编程】 第8章 Spark MLlib

系列文章目录 文章目录 系列文章目录前言【 第8章 Spark MLlib 】8.1 Spark MLlib简介8.1.1 什么是机器学习8.1.2 基于大数据的机器学习8.1.3 Spark 机器学习库MLLib 8.2 机器学习工作流8.2.1 机器学习流水线概念8.2.2 构建一个机器学习流水线 8.3 特征抽取、转化和选择8.4 分类…

【DBA日常工作职责---读书笔记】

&#x1f448;【上一篇】 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 【下一篇】&#x1f449; &#x1f53b;DBA 的工作职责是什么&#xff1f;每天 DBA 应该做哪些工作&#xff1f;稳定环境中的 DBA 该如何成长与 优化&#xff1…

Jmeter插件PerfMon Metrics Collector安装使用及报错解决

Jmeter作为一个轻量级的性能测试工具&#xff0c;开源、小巧、灵活的特性使其越来越受到测试人员喜爱。在实际的项目中&#xff0c;特别是跨地区项目&#xff0c;排除合作方指定要求使用Loadrunner作为性能测试工具外&#xff0c;Jmeter会是首选。 本篇文章&#xff0c;就着重…

华为云DevCloud平台部署bootdo博客论坛实战【开发者专属集市】

华为云DevCloud平台部署bootdo博客论坛实战【开发者专属集市】 一、bootdo-blog开源博客介绍二、本次实践所用工具及平台三、购买华为RDS数据库1.购买RDS数据库2.查看RDS数据库状态 四、创建项目1.登录华为云台DevCloud平台2.新建项目3.查看新建项目4.创建代码仓库 五、配置数据…

老话新谈之缓存一致性

前言 缓存一致性常见的更新策略也比较多&#xff0c;如先更新数据库再更新缓存&#xff0c;先删缓存再更新数据库等等&#xff0c;我在理解的时候有些混乱&#xff0c;所以这个文章提供了一些理解上的技巧去理解缓存一致性。 为什么会有缓存一致性的问题 缓存与数据库是两套…

网络安全从业人员2023年后真的会被AI取代吗?

随着ChatGPT的火爆&#xff0c;很多人开始担心网络安全从业人员会被AI取代。如果说网络安全挖洞的话&#xff0c;AI可能真的能取代。但是网络安全不仅仅只是挖洞&#xff0c;所以AI只是能缓解网络安全人员不足的情况&#xff0c;但是是不会取代人类的作用的。 就拿最近很火的C…

【STL】priority_queue的使用及模拟实现

目录 前言 priority_queue的使用 功能解析 基本接口 写点题目 模拟实现 结构解析 插入删除 调整函数结合仿函数 仿函数介绍 结合使用 其他功能 接口补齐 迭代器区间构造 前言 &#x1f37e;打开 queue 头文件后&#xff0c;我们发现除了我们之前介绍过的普通队列…

用maven安装JUnit 5并运行单元测试

一&#xff1a;首先讲如何安装 JUnit 5 JUnit Platform JUnit Jupiter JUnit Vintage 如果不需要执行基于JUnit 3 和JUnit 4 的用例&#xff0c;那么JUnit Vintage就不需要安装。 1&#xff09;在pom文件dependencies的小节内增加如下依赖&#xff1a; <dependency>&l…

Docker安装ClickHouse22.6.9.11并与SpringBoot、MyBatisPlus集成

背景 上一篇文章CentOS6.10上离线安装ClickHouse19.9.5.36并修改默认数据存储目录记录了在旧版的操作系统上直接安装低版本 ClickHouse &#xff08;脱胎于俄罗斯头号搜索引擎的技术&#xff09;的过程&#xff0c;开启远程访问并配置密码&#xff1b; 其实通过 Docker 运行 …

一文读懂性能测试

01、常见概念 吞吐量&#xff08;TPS, QPS&#xff09; 简单来说就是每秒钟完成的事务数或者查询数。通常吞吐量大表明系统单位时间能处理的请求数越多&#xff0c;所以通常希望TPS越高越好 响应时间 即从请求发出去到收到系统返回的时间。响应时间一般不取平均值&#xff0…