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

协程执行顺序引发的问题

引言

在Golang中,因为协程执行的顺序是不固定的,如果不在代码里进行控制,可能就会导致预期外的输出。
本文通过分析一段代码的执行来介绍这种情况,以及可行的控制协程执行顺序的方法:

  1. sleep()
  2. waitGroup

实例分析

代码

func NewRingBuffer(inCh, outCh chan int) *ringBuffer {return &ringBuffer{inCh:  inCh,outCh: outCh,}
}type ringBuffer struct {inCh  chan intoutCh chan int
}func (r *ringBuffer) Run() {for v := range r.inCh {select {case r.outCh <- v:default:<-r.outCh // pop one item from outchanr.outCh <- v}}close(r.outCh)
}func main() {inCh := make(chan int)outCh := make(chan int, 4)rb := pkg.NewRingBuffer(inCh, outCh)go rb.Run()for i := 0; i < 10; i++ {inCh <- i}close(inCh)//time.Sleep(time.Millisecond * 50)for res := range outCh {fmt.Println(res)}
}

上面代码的作用是,声明ringBuffer环状缓冲区;主协程往inCh写10个数的同时,有一个协程异步的读取数据,并写到outCh中;
outCh是一个4缓冲区大小的channel,如果outCh没满就直接写入,否则把outCh的首部元素移除再添加;
因此,预期输出应该是:6、7、8、9。
但实际的输出是:5、6、7、8、9。

分析

多出来的“5”,是因为协程的执行顺序不可控,当主协程执行到

for i := 0; i < 10; i++ {inCh <- i
}close(inCh)

时,此时outCh中是5、6、7、8;run协程还没有继续执行,就开始遍历outCh:

for res := range outCh {fmt.Println(res)
}

然后输出阻塞后,run协程才继续执行,把9写到outCh中,因此最后的输出结果是5、6、7、8、9.

解决办法

sleep()

使用sleep()函数,能让当前协程让出CPU,暂停执行一段时间。
对于这种方法,可以对上面函数进行如下改造:

close(inCh)
time.Sleep(time.Millisecond * 50)
// 新增
for res := range outCh {fmt.Println(res)
}

WaitGroup

使用waitGroup可以实现协程间通信,对于这段例子,通过wg保证run()函数执行完后,在对outCh进行输出。
改造方法如下:

func (r *ringBuffer) RunWithWg(wg *sync.WaitGroup) {defer wg.Done()for v := range r.inCh {select {case r.outCh <- v:default:<-r.outCh // pop one item from outchanr.outCh <- v}}close(r.outCh)
}func TestRaceWithWg(t *testing.T) {inCh := make(chan int)outCh := make(chan int, 4)rb := pkg.NewRingBuffer(inCh, outCh)var wg sync.WaitGroupwg.Add(1)go rb.RunWithWg(&wg)go func() {for i := 0; i < 10; i++ {inCh <- i}close(inCh)}()wg.Wait()for res := range outCh {fmt.Println(res)}
}

相关文章:

  • 如何配置taro
  • C++STL 初阶(5)vector的简易实现(上)
  • [MRCTF2020]PixelShooter
  • Jenkins+K8s实现持续集成(一)
  • 晶谷电子器件烧结封装介质材料 绝缘用晶谷低温封接环保玻璃粉 耐压高
  • git stash Pop 后丢失,要如何找回?
  • python输入、输出和变量
  • Java——集合(一)
  • flex布局无法设置图片icon和文本垂直居中对齐问题
  • 新手必备:macOS上用Homebrew轻松安装MySQL
  • 程序员如何高效读代码?
  • android 彩虹进度条自定义view实现
  • 软考高级论文真题“论大数据lambda架构”
  • express+vue在线im实现【三】
  • docker的隔离机制
  • 08.Android之View事件问题
  • 230. Kth Smallest Element in a BST
  • angular2开源库收集
  • HashMap剖析之内部结构
  • JavaScript创建对象的四种方式
  • Python打包系统简单入门
  • Ruby 2.x 源代码分析:扩展 概述
  • Webpack 4x 之路 ( 四 )
  • win10下安装mysql5.7
  • 大数据与云计算学习:数据分析(二)
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 设计模式(12)迭代器模式(讲解+应用)
  • 我从编程教室毕业
  • 一份游戏开发学习路线
  • 译自由幺半群
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​io --- 处理流的核心工具​
  • ​iOS安全加固方法及实现
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • # centos7下FFmpeg环境部署记录
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • #知识分享#笔记#学习方法
  • $GOPATH/go.mod exists but should not goland
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (5)STL算法之复制
  • (LeetCode 49)Anagrams
  • (初研) Sentence-embedding fine-tune notebook
  • (二)fiber的基本认识
  • (二)构建dubbo分布式平台-平台功能导图
  • (二)原生js案例之数码时钟计时
  • (每日一问)操作系统:常见的 Linux 指令详解
  • (三)Honghu Cloud云架构一定时调度平台
  • (算法设计与分析)第一章算法概述-习题
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (正则)提取页面里的img标签
  • (转)Linux下编译安装log4cxx
  • (转)winform之ListView