【Golang】Go语言编程思想(六):Channel,第六节,并发编程模式
并发模式
下例重新对 channel 的用法进行回顾:
package mainimport ("fmt""math/rand""time"
)func msgGen(name string) chan string {c := make(chan string)go func(name string) { // 在这个 goroutine 当中向外发送数据i := 0for {time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)c <- fmt.Sprintf("service %s: message %d", name, i) // 生成消息, 传递给 channeli++}}(name)return c
}func main() {m1 := msgGen("service 1") // msgGen() 是一个生成器, 它会生成消息m2 := msgGen("service 2") // m1 和 m2 是两个相互独立的产生消息的服务// 生成的消息是哪里来的呢? 答案就在 msgGen() 的 go func() 当中// 生成的消息怎么读呢? 使用 for 开启无限循环for {fmt.Println(<-m1) // 使用 <- 从 channel 接收消息fmt.Println(<-m2)}
}
在上述代码当中,我们定义了一个消息生成器 msgGen,它返回的是 chan string,即收发类行为 string 的 channel。
在 main 函数中,我们定义了两个消息接收器,分别是 m1 和 m2,可以将 m1 和 m2 看作是与 msgGen 这个服务进行交互的句柄(handle)。
运行上述程序,得到的输出是交替的,显然可以进一步使用 select 来进行改进。
实际上同时等待多个服务有两种方法,一种方法是新建一个专门接受各个服务器消息的 channel,再从这个 channel 读取数据:
func fanIn(c1, c2 chan string) chan string {c := make(chan string)// 开启两个 goroutinego func() {for { // 第一个 goroutine 将 c1 的数据送给 cc <- <-c1}}()go func() {for { // 第二个 goroutine 将 c2 的数据送给 cc <- <-c2}}()return c
}func main() {m1 := msgGen("service 1") // msgGen() 是一个生成器, 它会生成消息m2 := msgGen("service 2") // m1 和 m2 是两个相互独立的产生消息的服务m := fanIn(m1, m2) // 使用 fanIn 接受两个 chan 的数据for {fmt.Println(<-m)}
}
还可以使用 select 来实现上面的 fanIn,优点是不需要再为每一个 channel 新开一个 goroutine:
func fanInBySelect(c1, c2 chan string) chan string {c := make(chan string)go func() {for {select { // 使用 select 接受多个 channel 的数据case m := <-c1: // 如果 c1 有数据,c <- m // 将数据送给 c1case m := <-c2: // 如果 c2 有数据c <- m}}}()return c
}
可以对我们初始的 fanIn 进行修改,使它可以接受任意数量的 channel 并对 channel 当中的信息进行汇总:
func fanIn(chans ...chan string) chan string {// 由于 channel 也是一等公民, 因此可以使用上述 ... 的方式来输入 chansc := make(chan string)for _, ch := range chans {go func(in chan string) {for {c <- <-in}}(ch) // 一定要显式地将 ch 作为参数输入到 goroutine 当中}return c
}func main() {m1 := msgGen("service 1") // msgGen() 是一个生成器, 它会生成消息m2 := msgGen("service 2") // m1 和 m2 是两个相互独立的产生消息的服务// 生成的消息是哪里来的呢? 答案就在 msgGen() 的 go func() 当中// 生成的消息怎么读呢? 使用 for 开启无限循环m3 := msgGen("service 3")m := fanIn(m1, m2, m3)for {fmt.Println(<-m)}
}
测试结果如下:

并发任务控制
基于 Golang 的并发编程,可以实现下述的几种并发控制:
- 非阻塞等待;
- 超时机制;
- 任务中断/退出;
- 优雅退出;
非阻塞等待
基于 select 的 default 实现非阻塞等待。编写一个名为 nonBlockingWait 的函数,输入是 chan string,输出是 string 和 bool。如果 chan 有输出值,使用 select 返回输出值和 true,否则使用 default 返回空串和 false:
func nonBlockingWait(c chan string) (string, bool) {// 实现非阻塞等待select {case m := <-c:return m, true // 等到了数据default: // 基于 default 实现非阻塞的等待return "", false // 只要没有等到, 就输出空串和 false}
}func main() {m1 := msgGen("service 1") // msgGen() 是一个生成器, 它会生成消息m2 := msgGen("service 2")for {fmt.Println(<-m1)if m, ok := nonBlockingWait(m2); ok {fmt.Println(m)} else {fmt.Println("no message from service 2")}}
}
超时机制
使用 time.Duration 和 time.After 来实现超时机制:
func timeoutWait(c chan string, timeout time.Duration) (string, bool) {select {case m := <-c: // 等到了 chan string 的数据return m, truecase <-time.After(timeout):return "", false}
}
func main() {m1 := msgGen("service1")for {if m, ok := timeoutWait(m1, 2*time.Second); ok {fmt.Println(m)} else {fmt.Println("timeout")}}
}
当超出规定的时间 2s 仍然没有数据发送到 m1,就会输出 timeout:

任务中断/退出
假定我们只希望 main 函数当中只有 5s 在接收信息:
func main() {done := make(chan struct{}) // 使用 done 通知 goroutine 主线程即将结束m1 := msgGen("service1", done) // 将 done 传递给 channelfor i := 0; i < 5; i++ {if m, ok := timeoutWait(m1, time.Second); ok {fmt.Println(m)} else {fmt.Println("timeout")}}done <- struct{}{}time.Sleep(time.Second)
}func msgGen(name string, done chan struct{}) chan string {c := make(chan string)go func(name string) { // 在这个 goroutine 当中向外发送数据i := 0for {select {case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):c <- fmt.Sprintf("service %s: message %d", name, i)case <-done: // 一旦接收到 done 的消息, 服务将结束fmt.Println("Done")return}i++}}(name)return c
}
优雅退出
将 done 变为双向的 channel 即可实现。当 goroutine 当中的 done 接收到停止信号时,开始优雅退出,退出完成后,done 再向外部发送信号告知退出完毕:
func msgGen(name string, done chan struct{}) chan string {c := make(chan string)go func(name string) { // 在这个 goroutine 当中向外发送数据i := 0for {select {case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):c <- fmt.Sprintf("service %s: message %d", name, i)case <-done:// 开始优雅退出fmt.Println("cleaning up")time.Sleep(2 * time.Second)fmt.Println("cleaning done")done <- struct{}{} // 退出完毕, 发送信号通知外部return}i++}}(name)return c
}func main() {done := make(chan struct{})m1 := msgGen("service1", done)for i := 0; i < 5; i++ {if m, ok := timeoutWait(m1, time.Second); ok {fmt.Println(m)} else {fmt.Println("timeout")}}done <- struct{}{}<-done // 接收到外部停止信号之后才继续向下进行
}
相关文章:
【Golang】Go语言编程思想(六):Channel,第六节,并发编程模式
并发模式 下例重新对 channel 的用法进行回顾: package mainimport ("fmt""math/rand""time" )func msgGen(name string) chan string {c : make(chan string)go func(name string) { // 在这个 goroutine 当中向外发送数据i : 0fo…...
unity打包web,如何减小文件体积,特别是 Build.wasm.gz
unity打包WebGL,使用的是wasw,最终生成的Build.wasm.gz体积很大,有6.5M,有几个方法可以稍微减小这个文件的大小 1. 裁剪引擎代码: 此步可将大小从6.5减小到 6.2(此项默认开启,只是改了裁剪等级…...
go引入skywalking
前置条件:安装好jdk11,linux服务器(centos7.9),go版本(我的是1.18,1.21都可以) 1.下载skywalking Downloads | Apache SkyWalking 2.下载agent源码 Downloads | Apache SkyWalkin…...
大华DSS数字监控系统 attachment_downloadAtt.action 任意文件下载漏洞复现
0x01 产品描述: 大华 DSS 数字监控系统是大华开发的一款安防视频监控系统,拥有实时监视、云台操作、录像回放、报警处理、设备管理等功能。0x02 漏洞描述: 大华DSS数字监控系统 attachment_downloadAtt.action接口存在任意文件读取漏洞,未经身份验证攻击者可通过该漏洞读取…...
qt 封装 调用 dll
这个目录下 ,第一个收藏的这个 ,可以用, 但是有几个地方要注意 第一.需要将dll的头文件添加到qt的文件夹里面 第二,需要在pro文件里面添加动态库路径 第三,如果调用dll失败,那么大概需要将dll文件放在e…...
Python使用Selenium库获取 网页节点元素、名称、内容的方法
我们要用到一些网页源码信息,例如获取一些节点的class内容, 除了使用Beautifulsoup来解析,还可以直接用Selenium库打印节点(元素)名称,用来获取元素的文本内容或者标签名。 例如获取下面的class的内容&am…...
系统安全——访问控制访问控制
访问控制 概念 什么是访问控制 access control 为用户对系统资源提供最大限度共享的基础上,对用户的访问权进行管理,防止对信息的非授权篡改和滥用 访问控制作用 保证用户在系统安全策略下正常工作 拒绝非法用户的非授权访问请求 拒绝合法用户越权…...
SQL Server 数据库还原到某个时点(完整恢复模式)
将 SQL Server 数据库还原到某个时点(完整恢复模式) 适用范围: SQL Server 本主题介绍如何使用 SQL Server Management Studio 或 Transact-SQL 将数据库还原到 SQL Server 中的某个时间点。 本主题仅与使用完整恢复模式或大容量日志恢复模…...
埃隆马斯克X-AI发布Grok-2大模型,快来体验~
引言 近年来,人工智能技术的快速发展推动了大语言模型的广泛应用。无论是日常生活中的智能助手,还是行业中的自动化解决方案,大语言模型都扮演着越来越重要的角色。2024年,X-AI推出了新一代的大模型——Grok-2,这款模…...
Python工厂设计模式:简化对象创建
Python工厂设计模式:简化对象创建 引言什么是工厂模式?简单工厂模式示例定义基类和子类创建工厂类使用工厂创建对象 优点使用场景总结 引言 在编程中,我们经常需要创建不同的对象,但有时创建对象的逻辑可能会变得复杂。工厂设计模…...
【隐私计算篇】隐私集合求交(PSI)原理深入浅出
隐私集合求交技术是多方安全计算领域的一个子问题,通常也被称为安全求交、隐私保护集合交集或者隐私交集技术等,其目的是允许持有各自数据集的双方或者多方,执行两方或者多方集合的交集计算,当PSI执行完成,一方或者两方…...
工作中常用的8种设计模式
前言 设计模式在我们日常的软件开发中无处不在,它们帮助我们编写更易扩展、更具可读性的代码。 今天结合我实际工作场景和源码实例,跟大家一起聊聊工作中最常用的8种设计模式,希望对你会有所帮助。 1. 单例模式 单例模式确保一个类只有一…...
Qwen 论文阅读记录
本文仅作自己初步熟悉大模型,梳理之用,慢慢会更改/增加/删除,部分细节尚未解释,希望不断学习之后,能够完善补充。若有同道之人,欢迎指正探讨。 关于后面的code-qwen and math-qwen,我个人认为依…...
自动驾驶:百年演进
亲爱的小伙伴们😘,在求知的漫漫旅途中,若你对深度学习的奥秘、JAVA 、PYTHON与SAP 的奇妙世界,亦或是读研论文的撰写攻略有所探寻🧐,那不妨给我一个小小的关注吧🥰。我会精心筹备,在…...
SSM 校园一卡通密钥管理系统 PF 于校园图书借阅管理的安全保障
摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装校园一卡通密钥管理系统软件来发挥其高效地信息处理的作用&a…...
什么叫中间件服务器?
什么叫中间件服务器?它在软件架构中扮演着怎样的角色?在现代应用程序开发中,中间件服务器的概念很多人对它并不太熟悉,但其实它的作用却不小。 中间件服务器是一种连接不同软件应用程序的中介。想象一下,在一个大型企…...
【docker】12. Docker Volume(存储卷)
什么是存储卷? 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。这就意味着,当我们在容器中的这个目录下写入数据时,容器会将其内容直接写入到宿主机上与此容器建立了绑定关系的目录。 在宿主机上…...
SpringBoot【八】mybatis-plus条件构造器使用手册!
一、前言🔥 环境说明:Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE 经过上一期的mybatis-plus 入门教学,想必大家对它不是非常陌生了吧,这期呢,我主要是围绕以下几点展开,重点给大家介绍 里…...
OpenAI直播发布第4天:ChatGPT Canvas全面升级,免费开放!
大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,专注于分享AI全维度知识,包括但不限于AI科普,AI工…...
自学高考的挑战与应对:心理调适、学习方法改进与考试技巧提升
一、自学参加高考的成功条件 (一)报名条件 基本要求 自学参加高考,首先需严格遵守国家的法律法规,这是参与高考的基本前提。具备高中同等学力是核心要素之一,意味着考生需通过自学掌握高中阶段的知识体系与学习能力…...
手把手调SerDes眼图:从FFE系数配置到示波器实测避坑指南
手把手调SerDes眼图:从FFE系数配置到示波器实测避坑指南 在高速数字电路设计中,SerDes(串行器/解串器)技术已经成为现代通信系统的核心。无论是数据中心的光模块,还是消费电子中的USB4接口,SerDes都扮演着关…...
从指标到版图:基于Cadence与gmid方法的两级运放实战设计
1. 两级运放设计入门:从指标到晶体管的思维转换 第一次接触两级运放设计时,我盯着性能指标表发呆了半小时。AV≥10M、CL10pf、SR10V/us这些数字就像天书,直到导师扔给我一本《模拟集成电路设计艺术》和一份Cadence使用手册。现在回想起来&…...
国产 KVM 兼容痛点及全国产定制方案
作为标准 KVM、军工加固 KVM 产品经理,在一些项目落地过程中,我发现一个普遍问题:国产服务器、国产系统越来越普及,但市面上绝大多数 KVM 切换器,兼容性问题频发,已经成为运维短板。一、当前 KVM 最常见的兼…...
从数据同步工具往后看,NineData 社区版 V5.0.0 这次补齐了什么
从数据同步工具和 ChatDBA 这类能力往后看,V5.0.0 更像一次连续补强,而不是单点加功能。再结合异构数据库迁移工具这类需求,链路扩展、迁移评估和智能诊断一起往前推,社区版的可用边界也随之往前走了一步。落地之前先看这套能力框…...
Maxwell 2D仿真进阶:从磁力线可视化到磁感应强度曲线分析
1. Maxwell 2D仿真基础与优势解析 第一次接触电磁场仿真时,我被各种专业术语和复杂的操作界面搞得晕头转向。直到发现Maxwell 2D这个神器,才真正体会到电磁仿真的魅力。相比于3D仿真,2D版本有个特别实用的功能——可以直接观察磁力线分布&…...
Python热重载工具Reloadium:原理、配置与实战避坑指南
1. 项目概述:重新定义Python热重载的开发体验如果你是一名Python开发者,无论是做Web后端、数据分析脚本还是机器学习模型训练,大概率都经历过这样的场景:修改了一行代码,保存文件,然后不得不手动停止当前运…...
SpringBoot配置加载顺序实战:从踩坑到精通,搞懂spring.profiles.active和spring.config.location
SpringBoot配置加载顺序实战:从踩坑到精通 在SpringBoot项目的开发与部署过程中,配置加载顺序往往是开发者最容易踩坑的环节之一。你是否遇到过本地测试正常,但打包部署后配置突然失效的情况?或者在不同环境间切换时,某…...
基于Claude API的全栈AI应用开发框架:从架构设计到生产部署
1. 项目概述与核心价值最近在折腾AI应用开发,特别是想把手头的一些想法快速落地成可交互的Web应用。相信很多开发者都有类似的痛点:大模型API调用起来简单,但要把想法变成一个功能完整、界面友好、还能稳定部署的应用,中间隔着一道…...
基于OpenTelemetry构建企业级可观测性:从设计到生产实践
1. 项目概述:从“黑盒”到“白盒”的工程实践在分布式系统、微服务架构乃至复杂的单体应用开发中,我们常常面临一个共同的困境:系统内部的状态如同一个“黑盒”。当线上服务出现响应缓慢、内存泄漏或偶发性错误时,传统的日志&…...
Android AI助手开发实战:基于MVVM与OpenAI API的AnywhereGPT项目解析
1. 项目概述与核心价值最近在折腾移动端AI应用,发现一个挺有意思的开源项目,叫AnywhereGPT-Android。简单来说,它就是一个让你能在Android手机上,通过调用OpenAI的API(比如GPT-3.5/4)或者本地部署的模型&am…...
