当前位置: 首页 > news >正文

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

 

 

相关文章:

  • 【正点原子STM32连载】 第二十九章 睡眠模式实验 摘自【正点原子】APM32E103最小系统板使用指南
  • 微服务自动化docker-compose
  • 软件测试实习生的最后一天,四小时四场技术面试(三)
  • 基于uniapp封装的card容器 带左右侧两侧标题内容区域
  • 安卓adb
  • PHP开发日志 ━━ 不同方法判断某个数组中是否存在指定的键名,测试哪种方法效率高
  • 2024.01.09.Apple_UI_BUG
  • Android Retrofit使用详情
  • 行云部署成长之路 -- 慢 SQL 优化之旅 | 京东云技术团队
  • useContext
  • RetryTemplate
  • c# 人脸识别的思路
  • 【C++】取整函数ceil(),floor(),round()
  • vue computed计算不到数组或者对象的变化
  • 什么是云服务器ECS - 云服务器 ECS - 阿里云
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • bootstrap创建登录注册页面
  • express + mock 让前后台并行开发
  • JDK 6和JDK 7中的substring()方法
  • linux学习笔记
  • MySQL的数据类型
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • python_bomb----数据类型总结
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 分布式任务队列Celery
  • 类orAPI - 收藏集 - 掘金
  • 聊聊flink的BlobWriter
  • 嵌入式文件系统
  • 我从编程教室毕业
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 通过调用文摘列表API获取文摘
  • #Linux(权限管理)
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (八十八)VFL语言初步 - 实现布局
  • (二)构建dubbo分布式平台-平台功能导图
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (十)c52学习之旅-定时器实验
  • (学习日记)2024.02.29:UCOSIII第二节
  • (转)母版页和相对路径
  • (转)视频码率,帧率和分辨率的联系与区别
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .NET构架之我见
  • .NET框架类在ASP.NET中的使用(2) ——QA
  • [ C++ ] STL---仿函数与priority_queue
  • [145] 二叉树的后序遍历 js
  • [2023-年度总结]凡是过往,皆为序章
  • [Android开源]EasySharedPreferences:优雅的进行SharedPreferences数据存储操作
  • [Android学习笔记]ScrollView的使用
  • [Angular] 笔记 21:@ViewChild
  • [BetterExplained]书写是为了更好的思考(转载)
  • [BZOJ4337][BJOI2015]树的同构(树的最小表示法)
  • [CISCN 2023 初赛]go_session