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

自学高考的挑战与应对:心理调适、学习方法改进与考试技巧提升
一、自学参加高考的成功条件 (一)报名条件 基本要求 自学参加高考,首先需严格遵守国家的法律法规,这是参与高考的基本前提。具备高中同等学力是核心要素之一,意味着考生需通过自学掌握高中阶段的知识体系与学习能力…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...