多个Goroutine交替执行输出

文章目录

  • 多个Goroutine交替执行输出
    • 两个协程交替打印1-100的奇偶数
      • 使用channel作为信号传递实现
      • 使用runtime让协程竞争CPU
      • 使用sync的锁竞争实现
    • N个线程交替打印0-100
      • 没有严格的交替顺序的时候
      • 有严格交替顺序的时候
    • channel实现线程池
    • 参考资料

Go语言的协程goroutine在高并发方面具有优势,最简单的可以写一个简单的生产者消费者模型。

func mode1() {rand.Seed(time.Now().UnixNano())wgreceivers := sync.WaitGroup{}wgreceivers.Add(NumReceiver)datach := make(chan int)Max := 100000NumReceiver := 10// sendergo func() {for {if value := rand.Intn(Max); value == 0 {close(datach)return} else {datach <- value}}}()// receiverfor i := 0; i < NumReceiver; i++ {go func() {defer wgreceivers.Done()for value := range datach {fmt.Println(value)}}()}wgreceivers.Wait()
}

还有经典的两个线程交替输出的问题,例如:

  1. 两个线程交替打印 0~100 的奇偶数
  2. 用两个线程,一个输出字母,一个输出数字,交替输出 1A2B3C4D…26Z
  3. 通过 N 个线程顺序循环打印从 0 至 100

其实这类就是线程通信相关的问题。Go语言的多线程、并发相关的知识,给我的感觉就是就是用起来容易,控制起来很难,存在很多似是而非的过程。如果想要掌握Go的并发,除了多加练习之外,还需要查看一些优秀的实践(比如channel的优雅关闭、goroutine线程池的实现等等——文末会有相关参考的链接)。
下面会从两个线程交替打印奇偶数开始讲起,会使用channel实现,使用sync的Lock实现,还有使用runtime的Gosched控制CPU实现。进一步拓展到N个线程打印的。

两个协程交替打印1-100的奇偶数

使用channel作为信号传递实现

刚学习goroutine的时候可能会这样写:

func withChannel(num int) {ch := make(chan int)wg := sync.WaitGroup{}wg.Add(1)// sendergo func() {for i := 1; i <= num; {fmt.Println("sender:", i)i++ch <- ii++//time.Sleep(2 * time.Millisecond)//可以人为制造停顿,努力让结果适配  :)  }close(ch)}()// receivergo func() {defer wg.Done()for i := range ch {fmt.Println("receiver:", i)}}()wg.Wait()fmt.Println("stop")
}// output:
// sender: 1
// sender: 3
// receiver: 2
// receiver: 4
// ...

上面的函数利用channel承载值的传递(并没有作为信号传递)。修改方法可以将channel变为信号传递,控制协程输出的时机:

func withChannel2(num int) {ch1, ch2 := make(chan struct{}), make(chan struct{})wg := sync.WaitGroup{}wg.Add(2)fmt.Println("start print:")go func() {defer wg.Done()for i := 1; i <= num; i += 2 {<-ch1fmt.Println("one: ", i)ch2 <- struct{}{}}<-ch1// 如果这里没有这一步,会直接死锁 }()go func() {defer wg.Done()// for i := 2; i < num; i += 2 {  // 如果在这里没有= ,会直接死锁for i := 2; i <= num; i += 2 {<-ch2fmt.Println("two: ", i)ch1 <- struct{}{}}}()ch1 <- struct{}{} // 启动的信号wg.Wait()fmt.Println("stop the work!")
}

使用runtime让协程竞争CPU

直接使用runtime的Gosched让出CPU,达到线程阻塞。

func withCPU() {//设置可同时使用的CPU核数为1var wg sync.WaitGroup// 定义使用核心的数量的数量runtime.GOMAXPROCS(1)wg.Add(2)go func() {defer wg.Done()for i := 1; i < 101; i++ {//奇数if i%2 == 1 {fmt.Println("线程1打印:", i)}//让出cpuruntime.Gosched()}}()go func() {defer wg.Done()for i := 1; i < 101; i++ {// 偶数if i%2 == 0 {fmt.Println("线程2打印:", i)}// 让出cpuruntime.Gosched()}}()wg.Wait()
}

使用sync的锁竞争实现

这个部分实现的比较冗杂,因为要显式让线程睡眠,实现交替的效果,使用了sync包的Cond控制条件,signal唤醒线程,这里就存在资源竞争。比较冗余。如果有更好的办法,欢迎和我交流

Goroutine交替执行的相关问题与方法相关推荐

  1. PHP连接redis并执行redis相关命令的方法详解

    PHP连接redis并执行redis相关命令的方法详解 连接redis库的方法 共性的运算归类 redis服务类函数 set 操作增删改查 List栈的结构,注意表头表尾,创建更新分开操作 Set,没 ...

  2. shiro和Spring整合使用注解时没有执行realm的doGetAuthorizationInfo回调方法的解决

    shiro和Spring整合使用注解时没有执行realm的doGetAuthorizationInfo回调方法的解决 from :http://blog.csdn.net/babys/article/ ...

  3. python列表方法语句_Python中列表和元组的相关语句和方法讲解

    列表(list): 首先,列表属于序列,那么序列类型可用如下内建函数-- list(iter):把可迭代对象转换为列表. str(obj):把obj对象转换为字符串,即用字符串来表示这个对象. tup ...

  4. vs.net设计器里加载派生窗口类时要执行父窗口的Form_Load方法

    今天发现在vs.net设计器里加载派生窗口类时是要执行父窗口的Form_Load方法的,而在Form_Load里经常有一些业务逻辑,导致设计时加载派生窗口失败.解决的办法是在父窗口的Form_Load ...

  5. fanuc机器人编程手册_FANUC机器人示教编程:距离先执行指令功能介绍与使用方法

    概述 距离先执行指令是这样一种功能,它在机器人的TCP相对动作指令的目标位置到达所指定的距离以内时,与机器人的动作并行地调用程序,或者进行信号输出.本指令可作为动作指令的附加指令来使用,不能作为单独指 ...

  6. 【看了10年前我为本单位写的此篇文章,感慨万分】此文章题目为:为关于“执行力”相关要素的方案设想

    关于"执行力"相关要素的方案设想 摘要:鉴于***校长"执行力"相关精神及现有的软硬条件,在我校构建执行力解决方案的条件日渐成熟.基于SharePoint平台, ...

  7. 新颖且有价值的想法在哪里以及如何产生的?与产生相关的后颞叶和与执行控制相关的前额叶皮层的tDCS

    大家好,这里是"茗创科技".文章来源于"茗创科技"微信公众号,欢迎大家搜索关注. 现今社会突飞猛进的发展和进步离不开我们人类大脑所产生的一系列创造性的ideas ...

  8. Linux开机自动化执行脚本的四种方法(真实案例分享)

    Linux开机自动化执行脚本的四种方法(真实案例分享) 最近眼睛有点疼,可能是长时间面对电脑屏幕的原因罢.百度后安装了Redshift这款护眼工具,只要事先写好配置文件它会根据你的地理位置自动调节屏幕 ...

  9. Python列表(list)的相关操作及方法

    Python列表(list)的相关操作及方法 一.list列表 1.概述: 本质:list列表的本质是一种有序的集合 2.创建列表 语法: 列表名 = [元素1,元素2,元素3-] 说明:列表中的选项 ...

最新文章

  1. Linux中link,unlink,close,fclose详解
  2. 可逆加密算法 php,php可逆加密的方法及原理
  3. 一步一步将自己的代码转换为观察者模式
  4. centos 6.3 mysql python 模块_CentOS安装python2.6以及MySQL-python
  5. MFC多线程的创建,包括工作线程和用户界面线程
  6. 微信小程序引用php函数,微信小程序Page中data数据操作和函数调用详细介绍
  7. 《Effective Java》读书笔记三(类和接口)
  8. miniconda安装BWA 以及miniconda的环境配置
  9. 微信背后的产品观-学习笔记
  10. 记录:创建Django项目,在vscode中运行django项目
  11. 软件测试基础理论与测试方法
  12. Java六种异常处理的陋习
  13. 重装系统后,硬盘分区丢失的解决办法
  14. Layaverse掌舵人谢成鸿在上海静安国际设计节的主题分享
  15. 字号,行距,磅,像素……(word排版)
  16. 在线视频编辑 剪辑系统源码 支持AE所有特效
  17. 冷战时期_从冷战到深度学习的机器翻译历史
  18. 1949-2020年地级市全要素生产率(年度)
  19. 退休当月要干到月底吗_法律规定退休人员上班到退休当日还是当月
  20. python函数内嵌,嵌套函数

热门文章

  1. IP地址划分子网中 【计算子网掩码、可用地址数、每个划分的子网ip】的方法
  2. 《Spring Cloud与Docker微服务架构实战》读书笔记
  3. java web课程设计(简单商城的前后端双系统,基于maven三模块开发)
  4. Navicat的下载安全
  5. 通过 Windows XP Embedded 设备更新代理应用 QFE
  6. Java基础题37:(单选题)java中char类型的取值范围是() A.0 ... 32767 B.0 ... 65535
  7. 国内生产总值(GDP)数据可视化
  8. 计算机xp重装无声音怎么办,我的电脑重装WindowsXP后没有声音怎么办??
  9. 非规则浮点数(subnormal)引起浮点运算变慢
  10. vs code设置保存时自动将CRLF 转换成 LF