12.2 通道-阻塞与流程控制、通道型函数、退出通道

阻塞与流程控制

通常在并发程序中要尽力避免阻塞式操作,但有时又需要让代码暂时处于阻塞状态,以等待某种条件、信号或数据,然后再继续运行。

对于无缓冲通道,试图从无人写入的通道中读取,或者向无人读取的通道中写入,都会引起阻塞。

重点:利用无缓冲通道的阻塞I/O,可以很容易地在异步执行的多个Goroutine之间构建同步化的流程控制。

// 基于通道的流程控制
// 试图从无人写入的通道中读取,或者向无人读取的通道中写入,都会引起阻塞,利用
// 这一特性可在多"线程"之间建立某种"停——等"机制
// 在下例中,模拟了一个电子时间,每隔1s更新显示一次时间。在父线程中先于子线程写入而读取,产生阻塞;子线程先于父现场而写入,也会产生阻塞。
package mainimport ("fmt""time"
)func clock(c chan string) {ticker := time.NewTicker(time.Second) // 定时器,周期为1sfor { // 死循环t := <-ticker.C 	// C是一个chnnel,每间隔一个定时周期,可以从通道内// 度取1个时间信息// 格式化时间展示格式并写入传入函数的channel cc <- t.Format("02 Jan 2006 15:04:05")	}
}func main() {c := make(chan string)go clock(c)for {message := <-cfmt.Printf("\r%v", message)}
}

 通道型函数参数(只读、只写、可读可写)

可将通道作为参数传递给函数,并在其类型中指明该通道型参数是只读的、只写的,还是既可读又可写的。

func channelReader(c <-chan string) { // 只读通道message := <-c
}
func channelWriter(c chan<- string) { // 只写通道c <- "Hello World!"
}
func channelReaderAndWriter(c chan string) { // 可读写通道message := <-cc <- "Hello World!"
}

通过指定通道型参数的读写权限,有助于确保通道中数据的完整性,同时指定程序的哪部分可向通道发送数据,哪部分又能从通道接收数据。

select语句

在并发式编程中,经常需要利用多个通道,同时与多个Goroutine建立通信。

顺序遍历来自多个通道的消息显然并非好的设计,因为仅一个通道的阻塞就会影响对其它所有通道消息的处理,例如:

for {msg1 := <-c1 // fmt.Println(msg1)msg2 := <-c2 // fmt.Println(msg2)
}

假设负责向c1通道写入数据的"子线程"由于某种原因发生了阻塞,没能及时地写入数据,"父线程"将阻塞在从c1通道读取数据的语句,这时负责向c2通道写入数据的另外一个"子线程"将因为c2通道无人读取而发生写阻塞。这种因为一个"线程"发生阻塞导致所有"线程"都跟着一起阻塞的运行模式,显然有悖于并发式编程的设计初衷,应当着意避免。

// 多通道I/O(错误实例:顺序遍历)
// 顺序遍历来自多个通道的消息显然并非好的设计,因为
// 仅一个通道的阻塞就会影响对其它所有通道消息的处理
package mainimport ("fmt""time"
)func proc(c chan rune, ch rune,	// rune类型,unicode编码等价于int32ms time.Duration) {for {c <- ch time.Sleep(ms * time.Millisecond)}
}func main() {c1 := make(chan rune)c2 := make(chan rune)go proc(c1, '-', 100)	// 初衷:每100ms,打印1个-go proc(c2, '+', 500) // 初衷:每500ms,打印1个+for {ch := <-c1fmt.Printf("%c", ch)ch = <-c2fmt.Printf("%c", ch)}
}
// 打印输出:
// +-+-+-+-+-+-+-+-+-+-+-+-+ 
// 实际情况,两个现场都是按照500ms的时间间隔来打印的,其原因在于两个channel/// 的读取数据都发生在同一个线程中,且二者是顺序执行的关系,c2阻塞时,c1也无法
// 执行。

select语句为多个通道创建了一系列接收者,哪个通道有消息被写入先接收哪个通道。

for {select {case message := <-c1: // fmt.Println(message)case message := <-c2: // fmt.Println(message)}
}

"父线程"中的select语句以阻塞方式,同时监视连接着多个"子线程"的多个通道,无论哪个"子线程"向其所持有的通道写入了数据,select语句都会立即有所察觉,并根据先到先得的原则,匹配到与发生写入动作的通道相对应的case分支,读取该通道中的数据。

// 多通道选择 (前一示例的正确处理形式)
// select语句为多个通道创建了一系列接收者,
// 哪个通道先有消息被写入就先接收哪个通道
package mainimport ("fmt""time"
)func proc(c chan rune, ch rune,ms time.Duration) {for {c <- ch time.Sleep(ms * time.Millisecond)}
}
func main() {c1 := make(chan rune)c2 := make(chan rune)go proc(c1, '-', 100)go proc(c2, '+', 500)for {select {case ch := <-c1:fmt.Printf("%c", ch)case ch := <-c2:fmt.Printf("%c", ch)}}
}
// 打印输出:
// +-----+-----+-----+-----+ 

要想从多个通道中以最及时的方式接收并处理消息,select语句是个不错的选择,但如果所有的通道都没有消息呢?

  • select语句将会长时间甚至永远处于阻塞状态,这对于并发式编程同样是不利的。

可以设置一个超时时间,让select语句于指定的时间后解除阻塞,继续运行。

注:time包的After函数,其参数为某一时间值,该函数会返回1个channel。这个channel会在指定的参数时间之后,会有消息写入(一个时间消息)。

for {select {case message := <-c1:fmt.Println(message)case message := <-c2:fmt.Println(message)case <-time.After(time.Second): // 触发超时fmt.Println("反正也没消息,不如摸会鱼吧……╮(╯ω╰)╭ ")}
}

// 永久等待
// 若通道长时间无人写入,针对该 
// 通道的select语句将会一直阻塞
package main
import ("fmt""time"
)
func proc(c chan rune, ch rune,ms time.Duration) {for i := 0; ; { // 仅执行10次写入操作if i < 10 {c <- ch i++}time.Sleep(ms * time.Millisecond)}
}
func main() {c1 := make(chan rune)c2 := make(chan rune)go proc(c1, '-', 100) // 写10次-go proc(c2, '+', 500) // 写10次+for {select {case ch := <-c1:fmt.Printf("%c", ch)case ch := <-c2:fmt.Printf("%c", ch)}}
}
// 打印输出:
// +-----+-----++++++++ 
// 主线程在读取10个-与10个+后,就处于了永久阻塞状态。
// 等待超时
// 使用超时时间,可让select语句在长时间收不到消息的 
// 情况下不至于一直阻塞,可利用这段时间执行空闲处理
package main
import ("fmt""time"
)
func proc(c chan rune, ch rune,ms time.Duration) {for i := 0; ; {if i < 10 {c <- ch i++}time.Sleep(ms * time.Millisecond)}
}
func main() {c1 := make(chan rune)c2 := make(chan rune)go proc(c1, '-', 100)go proc(c2, '+', 500)for {select {case c := <-c1:fmt.Printf("%c", c)case c := <-c2:fmt.Printf("%c", c)case t := <-time.After(time.Second): // 触发超时,1sfmt.Printf("\n%s> Timeout!",t.Format("2006/01/02 15:04:05"))// ……还应有相应的退出循环,退出通道等善后操作}}
}
// 打印输出:
// +-----+-----++++++++
// 2020/01/04 16:45:57> Timeout!

退出通道

通过设置超时时间,固然可以解除处于阻塞状态的select语句,但有时解除阻塞的条件也许并不是时间。

为select语句添加一个退出通道,通过向退出通道发送消息解除select阻塞。

stop := make(chan bool)
go func() {if 消息循环可以退出了 {stop <- true}
}()
escape := false
for !escape { // 消息循环select {... // 处理各种消息case <-stop:escape = true}
} 
// 退出通道(给电子时钟的实例添加退出通道操作)
// 为select语句添加退出通道,向退出通道发送消息以结束select循环
package main
import ("fmt""time"
)
func clock(channel chan string) {ticker := time.NewTicker(time.Second)for {t := <-ticker.C channel <- t.Format("02 Jan 2006 15:04:05")}
}
func main() {work := make(chan string)stop := make(chan bool)go clock(work)go func() {time.Sleep(10 * time.Second) // 10s后关闭消息循环stop <- true}()escape := falsefor !escape {select {case message := <-work:fmt.Printf("\r%v", message)case <-stop:	// 退出通道escape = true}}fmt.Println("\nTime's up!")
}
// 打印输出:
// 04 Feb 2020 18:00:48
// Time's up! 

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

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

相关文章

SQL学习小记(三)

SQL学习小记&#xff08;三&#xff09; 功能实现思路代码部分名词解释 代码打包为可执行文件 功能说明&#xff1a;使用python代码&#xff0c;将数据库database1中的表格table1同步到数据库database2中 功能实现 思路 #mermaid-svg-R1pWrEWA799M299a {font-family:"tre…

Rocksdb原理简介

100编程书屋_孔夫子旧书网 Rocksdb作为当下nosql中性能的代表被各个存储组件&#xff08;mysql、tikv、pmdk、bluestore&#xff09;作为存储引擎底座&#xff0c;其基于LSM tree的核心存储结构&#xff08;将随机写通过数据结构转化为顺序写&#xff09;来提供高性能的写吞吐时…

Visual Studio 的使用

目录 1. 引言 2. 安装和配置 2.1 系统要求 2.2 安装步骤 2.3 初次配置 3. 界面介绍 3.1 菜单栏和工具栏 3.2 解决方案资源管理器 3.3 编辑器窗口 3.4 输出窗口 3.5 错误列表 3.6 属性窗口 4. 项目管理 4.1 创建新项目 4.2 导入现有项目 4.3 项目属性配置 5. 代…

当传统文化遇上数字化,等级保护测评的必要性

第二十届中国&#xff08;深圳&#xff09;国际文化产业博览交易会5月23日在深圳开幕。本届文博会以创办20年为契机&#xff0c;加大创新力度&#xff0c;加快转型升级&#xff0c;着力提升国际化、市场化、专业化和数字化水平&#xff0c;不断强化交易功能&#xff0c;打造富有…

《软件方法(下)》8.3.4.6 DDD话语“聚合”中的伪创新(1)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 8.3 建模步骤C-2 识别类的关系 8.3.4 识别关联关系 8.3.4.6 DDD话语“聚合”中的伪创新 DDD话语中也有“聚合”。Eric Evans的“Domain-Driven Design: Tackling Complexity in the…

在今日头条上写文章:ChatGPT完整使用教程

了解如何充分运用ChatGPT进行创作 简介 在今日头条上发布文章变得越来越方便。本文旨在详细解析如何运用ChatGPT来创作文章&#xff0c;并提供全方位的使用指南及常见问题的答疑。 第一步&#xff1a;基础准备 确保你已注册今日头条账号。 登录ChatGPT并与你的今日头条账号进…

软件测试经理工作日常随记【6】-利用python连接禅道数据库并自动统计bug数据到钉钉群

测试管理_利用python连接禅道数据库并统计bug数据到钉钉 这篇不多赘述&#xff0c;直接上代码文件。 另文章基础参考博文&#xff1a;参考博文 加以我自己的需求优化而成。 统计的前提 以下代码统计的前提是禅道的提bug流程应规范化 bug未解决不删除bug未关闭不删除 db_…

LuatOS学习

开发顺序 Lua是脚本语言中运行速度最快的语言 资源占用极低 脚本语言运行方式 脚本语言是从上往下一行一行运行的 变量 coun 123456 a,b,c 1,2,3交换 a,b b,a在测试环境中&#xff0c;用print(a,b)打印 nil类型 未声明的变量就是nil&#xff0c;nil用来表示此变量为空…

STM32高级控制定时器(STM32F103):检测输入PWM周期和占空比

目录 概述 1 PWM 输入模式 1.1 原理介绍 1.2 应用实例 1.3 示例时序图 2 使用STM32Cube配置工程 2.1 软件环境 2.2 配置参数 2.3 生成项目文件 3 功能实现 3.1 PWM占空比函数 3.2 输入捕捉回调函数 4 功能测试 4.1 测试软件框架结构 4.2 实验实现 4.2.1 测试实…

视觉语音识别挑战赛 CNVSRC 2024

CNVSRC 2024由NCMMSC 2024组委会发起&#xff0c;清华大学、北京邮电大学、海天瑞声、语音之家共同主办。竞赛的目标是通过口唇动作来推断发音内容&#xff0c;进一步推动视觉语音识别技术的发展。视觉语音识别&#xff08;也称为读唇技术&#xff09;是一种通过观察唇部动作推…

二叉树顺序结构实现【堆的实现】【详细图解】

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 目录 1、二叉树的顺序结构2、堆的概念3、堆的实现3.1 堆实现的前提3.1.1 向上调整3.1.2 向下调…

采用java语言+B/S架构+后端SpringBoot前端Vue开发的ADR药品不良反应智能监测系统源码

采用java语言&#xff0b;B/S架构&#xff0b;后端SpringBoot前端Vue开发的ADR药品不良反应智能监测系统源码 ADR监测引擎每日主动获取检验数据、病历内容&#xff08;可拓展&#xff09;、以及其他临床数据&#xff0c;根据知识库内容自动判定患者是否有不良反应迹象&#xf…

【iOS】UI学习(一)

UI学习&#xff08;一&#xff09; UILabelUIButtonUIButton事件 UIViewUIView对象的隐藏UIView的层级关系 UIWindowUIViewController定时器与视图对象 UISwitch UILabel UILabel是一种可以显示在屏幕上&#xff0c;显示文字的一种UI。 下面使用代码来演示UILabel的功能&#…

做好开源快速开发平台研发创新 助力行业高效发展!

随着信息化时代的到来&#xff0c;科技的力量无处不在。为了提高办公效率&#xff0c;很多大中型企业倾向于使用更为先进的软件平台来助力企业降本增效。在众多助力神器之中&#xff0c;开源快速开发平台低代码技术平台深得广大新老客户朋友的喜爱&#xff0c;它与生俱来的优势…

Java数据类型

一、每种数据都定义了 明确的数据类型&#xff0c;在内存中分配了不同大小的 内存空间(字节)。 二、Java数据类型分为两种&#xff1a; 基本数据类型&#xff1a; 数值型&#xff1a; 整数类型&#xff0c;存放整数(byte[1] , short[2] , int[4] , long[8]) 浮点类型&#xff0…

UE5 读取本地图片并转换为base64字符串

调试网址&#xff1a;在线图像转Base64 - 码工具 (matools.com) 注意要加&#xff08;data:image/png;base64,&#xff09; FString UBasicFuncLib::LoadImageToBase64(const FString& ImagePath) {TArray<uint8> ImageData;// Step 1: 读取图片文件到字节数组if (!…

【蓝桥杯】第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组

答题结果页 - 蓝桥云课 (lanqiao.cn) 0子2023 - 蓝桥云课 (lanqiao.cn)&#xff08;暴力枚举 #include<bits/stdc.h> using lllong long; using ullunsigned long long; #define fir first #define sec second //#define int llconst int N1e510; const int mod1e97;int…

HT46R002 贴片 SOP8 经济型AD型OTP MCU单片机芯片

HT46R002在智能家居中的具体应用案例可以包括以下几个方面&#xff1a; 1. 智能照明控制&#xff1a;可以用于控制LED灯的亮度和色温&#xff0c;甚至可以通过手机APP远程控制开关和调节灯光效果。 2. 环境监测&#xff1a;用于监测室内温度、湿度、空气质量等&#xff0c;当检…

httpJVM

目录 HTTPS如何保证安全 1&#xff09;引入非对称加密 2&#xff09;引入非对称加密 3.中间人攻击 4.解决中间人攻击 JVM 1.JVM内存划分 2.JVM类加载过程 八股内容 3.JVM中的垃圾回收机制 释放垃圾的策略 1.标记-清除 2.复制算法 3.标记-整理 分代回收 HTTPS如何…

Android Graphics图形栈SurfaceFlinger之间各种Layer以及对应Buffer之间的关系

Android Graphics图形栈SurfaceFlinger之间各种Layer以及对应Buffer之间的关系 SurfaceFlinger layer之间的对应关系