002 Golang-channel-practice

第二题:

创建一个生产器和接收器,再建立一个无缓冲的channel。生产器负责把数据放进管道里,接收器负责把管道里面的数据打印出来。这里我们开5个协程把数据打印出来。

直接上代码!

package mainimport ("fmt"
)func receive(c <-chan int) {/*for v := range c {fmt.Println("received:", v)}*/for i := 0; i <= 1; i++ {go func() {for v := range c {fmt.Println(v)}}()}
}
func generator() <-chan int {c := make(chan int)for i := 0; i <= 9; i++ {go func(i int) {for j := 0; j <= 9; j++ {temp := i*100 + 20 + jc <- temp}close(c)}(i)}return c
}
func main() {c := generator()receive(c)
}

埋了个小坑,跑上面的代码,在这里是不会有任何输出的。

87349339b46d4558b9a0c71f976ee5a4.png

原因是main函数结束时程序就退出了,没有给goroutine足够运行的时间来打印输出。

整个流程是并发执行的,main函数、generator的goroutine、receive的goroutine都是并发运行。

但是问题是main函数和generator很快就结束了,程序退出,receive的goroutine来不及打印数据。

解决方法就是让main函数等一等receive的goroutine。我们在main函数中加上一句:

time.Sleep(time.Second * 5) 

这时看到可以顺利输出了。

但是...

f0e9170ff1914f46baf6820c0b15c56c.png

但是却panic了。为什么呢?

因为generator()把消息发送到了关闭的管道。是因为生成器goroutine和接收goroutine的生命周期没有控制好导致的。

主要原因在于,接收的goroutine一旦从通道接收完所有的数据并退出,通道就会被关闭。

而此时,生成器goroutine可能还在向这个通道发送数据,于是产生了panic。

要避免这种情况,需要确保:

 

1、接收goroutine在最后一个生成器goroutine退出之前不能退出。

2、生成器goroutine在关闭通道之前,必须保证接收goroutine仍在运行。

 

问题出在生成器中close(c)这一行。这里每个goroutine都在自己完成后关闭了通道c。

按照程序逻辑,通道c应该在最后一个goroutine完成时关闭一次,而不是每个goroutine都关闭。所以应该只在主goroutine中关闭c。这里我们用WaitGroup来同步。

 

func generator() <-chan int {c := make(chan int) var wg sync.WaitGroupwg.Add(10) // 添加10个goroutinefor i := 0; i < 10; i++ {go func() {// 生成数据 wg.Done() // goroutine结束}()} go func() {wg.Wait()   // 等待所有goroutine完成close(c) // 关闭通道,仅关闭一次 }()return c 
}

 

顺利输出!!

e5ee8c796d6e454c9e30b504158688a3.png

 

 

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

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

相关文章

java基础之函数

函数 概念 是一段具有特定功能的代码, 特点为可以多次执行.通常情况下一个函数对应一个功能 语法 访问修饰符 static 返回值类型 函数名(形参列表){//操作语句 } public static void 函数名(){} 位置 类以内,其他函数以外,与主函数平级 调用 自定义函数必须经过调用才…

Linux操作系统——进程控制(三) 进程程序替换

前言 目前我们接触到我们所创建的所有的子进程&#xff0c;它执行的代码都是父进程代码的一部分&#xff01;那么如果我们想让子进程执行新的程序呢&#xff1f;&#xff1f;&#xff1f;执行全新的代码和访问全新的数据&#xff0c;不在和父进程有瓜葛&#xff0c;我们该怎么…

Redis缓存使用问题

数据一致性 只要使用到缓存,无论是本地内存做缓存还是使用 redis 做缓存,那么就会存在数据同步的问题。 以 Tomcat 向 MySQL 中写入和删改数据为例,来解释数据的增删改操作具体是如何进行的。 我们分析一下几种解决方案, 1、先更新缓存,再更新数据库 2、先更新数据库,…

【JVM】本地方法接口 Native Interface

一、JNI简介 JVM本地方法接口&#xff08;Java Native Interface&#xff0c;JNI&#xff09;是一种允许Java代码调用本地方法&#xff08;如C或C编写的方法&#xff09;的机制。这种技术通常用于实现高性能的计算密集型任务&#xff0c;或者与底层系统库进行交互。 二、JNI组…

Zynq 电源

ZYNQ芯片的电源分PS系统部分和PL逻辑部分&#xff0c;两部分的电源分别是独立工作。PS系统部分的电源和PL逻辑部分的电源都有上电顺序&#xff0c;不正常的上电顺序可能会导致ARM系统和FPGA系统无法正常工作。 PS部分的电源有VCCPINT、VCCPAUX、VCCPLL和PS VCCO。 VCCPINT为PS内…

【GoLang入门教程】Go语言几种标准库介绍(六)

文章目录 前言几种库Net库 (网络库&#xff0c;支持 Socket、HTTP、邮件、RPC、SMTP 等)重要的子包和功能&#xff1a;示例 OS库&#xff08;操作系统平台不依赖平台操作封装&#xff09;主要功能&#xff1a;示例 path库(兼容各操作系统的路径操作实用函数)常用函数&#xff1…

Elasticsearch 地理空间搜索 - 远超 OpenSearch

作者&#xff1a;来自 Elastic Nathan_Reese 2021 年&#xff0c;OpenSearch 和 OpenSearch Dashboards 开始作为 Elasticsearch 和 Kibana 的分支。 尽管 OpenSearch 和 OpenSearch Dashboards 具有相似的血统&#xff0c;但它们不提供相同的功能。 在分叉时&#xff0c;只能克…

【ArcGIS微课1000例】0087:经纬度格式转换(度分秒转度、度转度分秒)

ArcGIS软件可以很方便的直接实现度分秒转度、度转度分秒。 文章目录 一、转换预览二、工具介绍三、案例解析一、转换预览 借助ArcGIS快速实现度分秒与度及其他格式的坐标转换。例如:度分秒→度 度分秒: 度: 二、工具介绍 转换坐标记法:将一个或两个字段包含的坐标记法从一…

uniapp项目怎么删除顶部导航栏

uniapp去掉顶部导航的方法&#xff1a; 1、去掉所有导航栏 "globalStyle": { "navigationBarTextStyle": "white", "navigationBarTitleText": "uni-app", "navigationBarBackgroundColor": "#007AFF"…

Digital Audio (HDMI)未插入 用Hdmi连接电脑 显示高清数字音频未插入 win10电脑没声音,喇叭上一个叉❌

先说结论&#xff0c;出现这些问题的原因&#xff1a; 未插入音频设备或者硬件问题&#xff08;10%&#xff09;设置错误&#xff0c;未使用显示器音频 &#xff08;30%&#xff09;音频驱动不兼容或者没有驱动&#xff08;50%&#xff09;其他驱动有问题 &#xff08;10%&…

OpenCV——多分辨率LBP的计算方法

目录 一、算法原理1、原理概述2、参考文献 二、代码实现三、结果展示 OpenCV——多分辨率LBP的计算方法由CSDN点云侠原创&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫。 一、算法原理 1、原理概述 基本LBP算子虽然在早期…

TensorFlow相关组件的安装

安装Anaconda3 安装Anaconda3的时候可以安装在任意磁盘中&#xff0c;在勾选path的时候全部勾选即可。更换Anaconda3的下载源为清华大学源&#xff0c;在此期间不要打开Anaconda3&#xff0c;在cmd中依次输入以下即可&#xff1a; conda config --add channels https://mirro…