go语言并发编程(四) ——再探管道

单向管道

什么是单向管道

在Go语言中,管道有两种类型:双向管道与单向管道.双向管道指的是可以读也可以写,能在管道两边进行数据的读写操作,而单向管道指的是只能在管道的一边进行操作,我们手动创建一个只读/写的管道意义不大,一般是用于函数的参数传递或是作为返回值出现,例如我们用来关闭协程的管道的函数:

func close(c <- chan T)

双向管道可以转换为单向管道,反过来则不可以。通常情况下,将双向管道传给某个协程或函数并且不希望它读取/发送数据,就可以用到单向管道来限制另一方的行为。

func main() {
ch := make(chan int, 1)
go write(ch)
fmt.Println(<-ch)
}func write(ch chan<- int) {
// 只能对管道发送数据
ch <- 1
}

当然读管道同理

for range 遍历管道

在Go语言中我们可以基于for range来遍历读取管道中的数据,比如下面的这个例子:

package mainimport "fmt"func main() {ch := make(chan int, 10)defer close(ch)for i := 0; i < 10; i++ {ch <- i}for i := 0; i < 10; i++ {fmt.Println(<-ch)}
}

输出为:

0
1
2
3
4
5
6
7
8
9

其实通常来说当我们使用 for range来遍历数据结构的时候,一般会有两个返回值:一个是索引,另一个则是该索引对应的位置的元素值,但是对于管道而言,有且仅有一个返回值,就是缓冲区的元素值,for range会遍历读取管道缓冲区中的元素,当管道缓冲区为空时,就会阻塞等待,直到有其他协程向管道中写入数据才会继续读取数据。

注意:关闭管道的操作尽量在发送数据的那一方进行,而不要在接收方关闭管道,因为大多数情况下接收方只知道接收数据,并不知道该在什么时候关闭管道。

select

什么是select

select在Linux系统中一般用于IO多路复用,类似的,在Go语言中,select是一种管道多路复用的控制结构,而到底什么是多路复用,简单的用一句话来概括的话就是:在某一时刻,同时监测多个元素是否可用,而被监测的可以是网络请求,文件IO等,而在Go语言中的select则用于检测管道是否可用,如果可用,则读取管道中的数据,否则阻塞等待。
接下来我们来看一个简单的select的例子:

package mainimport "fmt"func main() {ch1, ch2, ch3 := make(chan bool), make(chan bool), make(chan bool)defer func() {close(ch1)close(ch2)close(ch3)}()select {case n, ok := <-ch1:fmt.Println(n, ok)case n, ok := <-ch2:fmt.Println(n, ok)case n, ok := <-ch3:fmt.Println(n, ok)default:fmt.Println("default")}
}

与switch相似,select由多个case和一个default组成,default分支可以忽略,而每一个case只能操作一种管道,并且只能进行一种操作,要么读要么写,当有多个case可用的时候,select会随机选择一个case来执行,如果所有case都不可用,就会执行default分支,倘若没有default分支,将会阻塞等待,直到至少有一个case可用。所以上面的输出结果
为:

default

不过上述的的例子中,执行完对应分支以后,主协程就直接退出了,所以如果我们想一直检测管道的话,要给select语句加上一个死循环代码来保证select可以一直监测管道:

package mainimport "fmt"func main() {ch1, ch2, ch3 := make(chan int), make(chan int), make(chan int)defer func() {close(ch1)close(ch2)close(ch3)}()go Send(ch1)go Send(ch2)go Send(ch3)for {select {case n, ok := <-ch1:fmt.Println(n, ok)case n, ok := <-ch2:fmt.Println(n, ok)case n, ok := <-ch3:fmt.Println(n, ok)}}}func Send(ch chan<- int) {for i := 0; i < 3; i++ {ch <- i}
}

这样确实三个管道都能用上了,但是死循环+select会导致主协程永久阻塞,所以可以将其单独放到新协程中,并且加上一些其他的逻辑。

package mainimport ("fmt""time"
)func main() {chan1, chan2, chan3 := make(chan int), make(chan int), make(chan int)l := make(chan struct{})defer func() {close(chan1)close(chan2)close(chan3)}()go Send(chan1)go Send(chan2)go Send(chan3)go func() {Loop:for {select {case n, ok := <-chan1:fmt.Println("A", n, ok)case n, ok := <-chan2:fmt.Println("B", n, ok)case n, ok := <-chan3:fmt.Println("C", n, ok)case <-time.After(1 * time.Second): //设置超时时间break Loop //}}l <- struct{}{}}()<-l
}func Send(ch chan<- int) {for i := 0; i < 3; i++ {ch <- i}
}

上例中通过for循环配合select来一直监测三个管道是否可以用,并且第四个case是一个超时管道,超时过后便会退出循环,结束子协程。最终输出如下:
输出结果为:

A 0 true
B 0 true
B 1 true
A 1 true
A 2 true
C 0 true
B 2 true
C 1 true
C 2 true

超时机制

在是一个例子中我们用到了time.After函数,这个函数的返回值是一个只读的管道,我们可以利用它来实现一个比较简单的超时机制,比如下面这个例子:

package mainimport "time"func main() {ch := make(chan struct{}, 1)go func() {time.Sleep(time.Second * 2)ch <- struct{}{}}()Loop:for {select {case <-ch:println("ok")case <-time.After(time.Second):println("超时")break Loop}}
}

输出为:

超时

注意:在select的case中对值为nil的管道进行操作的话,并不会导致阻塞,该case则会被忽略,永远也不会被执行

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

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

相关文章

ISO体系介绍

ISO体系太多太乱&#xff0c;搞不清该做哪个&#xff1f;没关系&#xff01;今天就来给大家挨个解读一下&#xff0c;哪些企业应该做什么样的体系认证最合适。不花冤枉钱&#xff0c;也别漏掉了需要的证书啦&#xff01; 一、ISO9001质量管理体系 ISO9001标准是一个放之四海皆…

网站建设也会涉及商标侵权,需要注意些!

以前普推知产老杨碰到建站涉及知识产权侵权的&#xff0c;但是大多数是其它方面的&#xff0c;前几天看到某同行说由于给客户建设网站&#xff0c;由于网站名称涉及商标被起诉要索赔几十万。 当时同行给做网站时还看了下营业执照&#xff0c;上面的主体名称与网站名称也是一致…

上海计算机学会 2023年10月月赛 丙组T2 颁奖典礼(思维)

第二题&#xff1a;T2颁奖典礼 标签&#xff1a;思维题意&#xff1a; n n n个学生参加颁奖典礼&#xff0c;学号为 1 1 1到 n n n。学生根据获奖等第依次上台领奖。已知学号为 i i i的学生是第 a i a_i ai​个上台领奖的。输出学生上台领奖的学号顺序。题解&#xff1a;以第…

乡村智慧化升级:数字乡村打造农村生活新品质

目录 一、乡村智慧化升级的内涵与意义 二、乡村智慧化升级的具体实践 1、加强农村信息基础设施建设 2、推广智慧农业应用 3、提升乡村治理智慧化水平 4、丰富智慧乡村生活内容 三、数字乡村打造农村生活新品质的成果展现 1、农业生产效率与质量双提升 2、农民收入与消…

汽车标定技术(十九) -- 移植标定栈时主机厂、供应商应该做什么?(1)

目录 1. 供应商视角 1.1 确认需求 1.2 代码移植阶段 1.3 考虑标定参数实现方式

主干网络篇 | YOLOv8更换主干网络之VanillaNet | 华为方舟实验室提出全新轻量级骨干架构

前言:Hello大家好,我是小哥谈。华为方舟实验室所提出的VanillaNet架构克服了固有复杂性的挑战,使其成为资源受限环境的理想选择。其易于理解和高度简化的架构为高效部署开辟了新的可能性。广泛的实验表明,VanillaNet提供的性能与著名的深度神经网络和vision transformers相…

【数学】主成分分析(PCA)的详细深度推导过程

本文基于Deep Learning (2017, MIT)&#xff0c;推导过程补全了所涉及的知识及书中推导过程中跳跃和省略的部分。 blog 1 概述 现代数据集&#xff0c;如网络索引、高分辨率图像、气象学、实验测量等&#xff0c;通常包含高维特征&#xff0c;高纬度的数据可能不清晰、冗余&am…

开源项目one-api的k8s容器化部署(上)-- 制作镜像及部署准备

一、背景 最近需要对开源项目one-api进行k8s容器化部署&#xff0c;主要分以下几个步骤&#xff1a; 制作docker镜像申请mysql和redis数据库docker-compose部署方式k8s部署方式 整个的篇幅比较长&#xff0c;将会分成上下两篇来阐述。 二、制作docker镜像 开源项目one-api…

2024 年(第 12 届)“泰迪杯”C 题:竞赛论文的辅助自动评阅

一、问题背景 近年来我国各领域各层次学科竞赛百花齐放&#xff0c;层出不穷&#xff0c;学生参与度也越来越高。随着参赛队伍的增加&#xff0c;评阅论文的工作量急剧增加&#xff0c;这对评阅论文的人力要求也越来越大。因此引入机器辅助评阅成为竞赛主办方的现实需求。 在…

Linux的学习之路:9、冯诺依曼与进程(1)

摘要 本章主要是说一下冯诺依曼体系结构和进程的一部分东西。 目录 摘要 一、冯诺依曼体系结构 二、操作系统的概念 三、设计OS的目的 四、管理 五、进程的基本概念 六、PCB 七、在Linux环境下查看进程 八、使用代码创建进程 九、思维导图 一、冯诺依曼体系结构 如…

Linux adduser命令教程:如何添加新用户(附实例详解和注意事项)

Linux adduser命令介绍 adduser是Linux系统中的一个命令行工具&#xff0c;用于创建新的用户。它是useradd这个底层工具的一个更友好的前端。当添加新用户时&#xff0c;它还会在/home目录下创建用户目录。 Linux adduser命令适用的Linux版本 adduser命令在大多数Linux发行版…

5G Frequency Bands 频率分布

连接&#xff1a;https://www.5g-networks.net/5g-technology/5g-frequency-bands/

每日一题:两数之和

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回…

uniapp:uview-plus的一些记录

customStyle 并不是所有的组件都有customStyle属性来设置自定义属性&#xff0c;有的还是需要通过::v-deep来修改内置样式 form表单 labelStyle 需要的是一个对象 :labelStyle"{color: #333333,fontSize: 32rpx,fontWeight: 500}"dateTimePicker选择器设置默认值…

最新视频理解大模型之MiniGPT4-video

前言 随着大模型的爆火&#xff0c;多模态大模型也随之卷了起来&#xff0c;基本每隔一小段时间就会冒出一个新模型。 今天给大家带来一个最新发现的关于视频理解的多模态大模型。 它的名字是MiniGPT4-video&#xff0c;可以看的出来其是MiniGPT4的一个分支&#xff1b;Mini…

root管理员用户启动kibana报错

问题描述: CentOS7.9.2009环境,以root管理员用户启动kibana7.11.1程序报如下错误: Kibana should not be run as root. Use --allow-root to continue. [root@elasticsearch bin]# whoami root [root@elasticsearch bin]# pwd /usr/local/kibana-7.11.1-linux-x86_64/bi…

STM32利用软件I2C通讯读MPU6050的ID号

今天的读ID号是建立在上篇文章中有了底层的I2C通讯的6个基本时序来编写的。首先需要完成的就是MPU6050的初始化函数 然后就是编写 指定地址写函数&#xff1a; 一&#xff1a;开始 二&#xff1a;发送 从机地址读写位&#xff08;1&#xff1a;读 0&#xff1…

ESP-IDF移植lvgl 驱动 ST7789

文章目录 1 前言2 准备3 移植LVGL3.1 工程准备3.2 修改 CMakeLists.txt文件编译 LVGL3.3 编译LVGL 4 编译 ST7789 LCD驱动5 发现问题 1 前言 本教程开始学习 LVGL的&#xff0c;开始之前要把环境配置好&#xff0c;首先就需要移植 lvgl&#xff0c;使用的是 esp32 环境&#xf…

【Vue】前端Crypto-js加密库md5加密转成二进制/十六进制/Base64格式

MD5是一种常用的哈希函数&#xff0c;用于生成数据的消息摘要&#xff0c;在前端开发中&#xff0c;我们经常需要对数据进行加密&#xff0c;以确保数据的安全性。 Crypto-js是一个常用的JavaScript加密库&#xff0c;它提供了MD5算法的实现&#xff0c;并且支持将加密结果转换…

C++_ 头指针在链表的操作中用来标识链表的起始位置

链表&#xff08;linked list&#xff09;是一种常见的数据结构&#xff0c;用于存储一系列元素。它由一系列节点组成&#xff0c;每个节点包含数据和指向下一个节点的指针。 在 C 中&#xff0c;可以使用结构体来表示链表节点&#xff0c;然后使用指针将这些节点连接起来。 -…