【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				// 接收到外部停止信号之后才继续向下进行
}

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

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

相关文章

重生之我在异世界学编程之C语言:深入结构体篇(上)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文《1》 结构体的两种声明一、结构…

Scala递归中求汉罗塔游戏的步骤

记&#xff1a;f(n,"A","B","C")表示n个盘子从A柱子上移动到C柱子上&#xff0c;借用B柱子的过程 f(要移动的盘子的个数&#xff0c;起点&#xff0c;辅助柱子&#xff0c;终点) 1.基本情况(直接能求的)&#xff1a;f(1,"A","B&…

输入url到显示主页的详细过程

从浏览器地址输入url到显示主页的过程&#xff1f; 主要分为&#xff1a;DNS解析&#xff0c;TCP连接&#xff0c;发送HTTP请求&#xff0c;服务器处理请求&#xff0c;浏览器接收HTTP响应&#xff0c;断开连接 DNS解析&#xff1a; 浏览器发起一个DNS请求到DNS服务器&#…

使用C#通过ColorMatrix对象为图像重新着色

此示例产生了一些令人印象深刻的结果&#xff0c;但实际上非常简单。 它使用其他几个示例演示的 ImageAttribute 技术来快速操作图像的颜色。 下面的AdjustColor方法启动图像着色的过程。 // Adjust the images colors. private Image AdjustColor(Image image) {// Make the …

手机租赁系统开发全流程解析与实用指南

内容概要 在如今快速发展的科技时代&#xff0c;手机租赁系统已经成为一种新兴的商业模式&#xff0c;非常符合当下市场需求。那么&#xff0c;在开发这样一个系统的时候&#xff0c;首先要从需求分析和市场调研开始。在这一阶段&#xff0c;你需要了解用户需要什么&#xff0…

unity打包web,如何减小文件体积,特别是 Build.wasm.gz

unity打包WebGL&#xff0c;使用的是wasw&#xff0c;最终生成的Build.wasm.gz体积很大&#xff0c;有6.5M&#xff0c;有几个方法可以稍微减小这个文件的大小 1. 裁剪引擎代码&#xff1a; 此步可将大小从6.5减小到 6.2&#xff08;此项默认开启&#xff0c;只是改了裁剪等级…

字符集编码

由于计算机只能存储和处理二进制的“0”和“1”&#xff0c;无法处理其他的字母、数字和符号&#xff0c;所以就需要有某种东西来达到类似桥梁的作用——例如图1-7中的ASCII——通过它&#xff0c;人们就可以看懂用计算机表示字母、数字或其他符号。 人们能够想到的最直接的方…

OceanBase 社区版 4.0 离线方式升级bp1至bp2 指南(含避坑总结)

注&#xff1a;目前社区版对 4.0 升级 bp1至 bp2也未有完善的文档&#xff0c;本次升级中也是遇到不少坑&#xff0c;写本文也希望对OB感兴趣的可以尝试少些遇坑。 也希望对升级有更好方式建议方式的朋友一起切磋交流&#xff0c;以便再进一步完善升级方案。 第一次做OB的升级&…

基于SSM框架的社区医院管理系统(计算机毕业设计)+万字说明文档

系统合集跳转 源码获取链接 一、系统环境 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 IDE环境&#xff1a; Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以 tomcat环境&#xff1a; Tomcat 7.x,8.x,9.x版本均可 操作系统…

视频安防监控平台:Liveweb视频监控管理云平台方案

LiveWeb是深圳市好游科技有限公司开发的一套综合视频汇聚管理平台&#xff0c;可提供多协议&#xff08;RTSP/RTMP/GB28181/海康Ehome/大华&#xff0c;海康SDK等&#xff09;的视频设备接入&#xff0c;支持GB/T28181上下级联&#xff0c;RTSP\RTMP转GB/T28181&#xff0c;云台…

Linux进程概念-详细版(二)

目录 前言&#xff1a; 进程优先级 基本概念 查看系统进程 PRI与NI 查看进程优先级信息 通过top命令更改进程的nice值 通过renice命令更改进程的nice值 四个重要概念 环境变量 基本概念 常见环境变量 查看环境变量的方法 测试PATH 测试HOME 测试SHELL ​编辑 …

前端监控方案sentry整体概览

查看PDF 目****录 1. Sentry介绍 1.1. 编写目的 1.2. 名词定义 2. Sentry监控原理概述 2.1. 常见的性能优化指标及获取方式 2.2. 常见的前端异常及其捕获方式 3. Sentry?整体架构 4. Sentry安装部署 4.1. 前提条件 4.2. 安装 5. Sentry环境配置 5.1. 初始化配置 …

C++11相关知识点

auto、decltype自动类型推导 auto并不代表实际的数据类型&#xff0c;且auto并不是在任何场合下都能推导出变量的实际类型&#xff0c;使用auto必须初始化。 auto 变量名 变量值&#xff1b; 当变量不是指针或引用类型时&#xff0c;推导的结果中不会保留const、volatile关键…

【面试】分布式锁专题

1.你说一下什么是分布式锁 分布式锁是一种在分布式系统环境下实现的锁机制&#xff0c;它主要用于解决&#xff0c;多个分布式节点之间对共享资源的互斥访问问题&#xff0c;确保在分布式系统中&#xff0c;即使存在有多个不同节点上的进程或线程&#xff0c;同一时刻也只有一…

Error relaunching VirtualBox VM process: 5 启动虚拟机时发生了错误

出现错误 一大早起来发现虚拟机打不开&#xff0c;看了虚拟机日志是正常的&#xff0c;还回了个档都不行。 最后我突然想起之前在哪看到过&#xff1a;“完美游戏平台会导致虚拟机的问题。” 解决方法 于是我把完美游戏卸载了&#xff0c;发现&#xff0c;真的&#xf…

MCU、ARM体系结构,单片机基础,单片机操作

计算机基础 计算机的组成 输入设备、输出设备、存储器、运算器、控制器 输入设备&#xff1a;将其他信号转换为计算机可以识别的信号&#xff08;电信号&#xff09;。输出设备&#xff1a;将电信号&#xff08;&#xff10;、&#xff11;&#xff09;转为人或其他设备能理解的…

从零开始:如何在.NET Core Web API中完美配置Swagger文档

目录 新建项目 RestFul Swagger配置 注释展示 版本控制 Token传值 方法封装 新建项目 打开visual studio创建新项目&#xff0c;这里我们选择.net core web api模板&#xff0c;然后输入项目名称及其解决方案创建新项目 这里使用配置一些其他信息&#xff0c;根据自己情…

百度搜索应适用中文域名国家标准,修复中文网址展示BUG

12月1日中文域名国家标准正式实施。该标准“明确了中文域名在编码、解析、注册、字表等方面的技术要求&#xff0c;适用于中文域名注册管理机构、注册服务机构、网络软硬件服务商及终端用户”。 00:23 显然&#xff0c;百度作为网络软硬件服务商&#xff0c;是包括在国家标准的…

Windows安装elasticsearch、Kibana以及IK分词器

一、下载 1.下载elasticsearch 访问官网Download Elasticsearch | Elastic&#xff0c;下载elasticsearch 2.下载 Kibana 访问Download Kibana Free | Get Started Now | Elastic &#xff0c;下载 Kibana 3. IK分词器下载 访问Gitee 极速下载/elasticsearch-analysis-ik选…

第一个C++程序--(蓝桥杯备考版)

第一个C程序 基础程序 #include <iostream>//头⽂件 using namespace std;//使⽤std的名字空间 int main()//main函数 {cout << "hello world!" << endl; //输出&#xff1a;在屏幕打印"hello world!" return 0;}main函数 main 函数是…