【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工…...

自学高考的挑战与应对:心理调适、学习方法改进与考试技巧提升
一、自学参加高考的成功条件 (一)报名条件 基本要求 自学参加高考,首先需严格遵守国家的法律法规,这是参与高考的基本前提。具备高中同等学力是核心要素之一,意味着考生需通过自学掌握高中阶段的知识体系与学习能力…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...