使用Golang的channel交叉打印两个数组的操作


Posted in Golang onApril 29, 2021

Go的channel提供了强大的同步功能,那么如何使用channel交叉打印两个数组呢?

灰常简单,只需设置两个channel变量

数组1打印完一个值就用channel通知数组2,同理数组2打印完一个值用另一个channel通知数组1,即可实现同步

package main 
import "fmt" 
func main(){
 ch1 :=make(chan int)
 ch2 :=make(chan string)
 str :=[5]string{"a","b","c","d","e"}
 go func() {
  for i:=0;i<5;i++{
   ch1<-i
   fmt.Print(i+1)
   <-ch2
  }
 }()
 
 for _,v :=range str{
  <-ch1
  fmt.Print(v)
  ch2<-v
 }
}

结果:

1a2b3c4d5e

Process finished with exit code 0

补充:使用golang的channel的坑

很多时候我们经过使用有缓冲channel作为通信控制的功能,以至有一些误解和坑出现。

误解一:有缓存channel是顺序的

执行下面代码:

package mainimport (    "time"
    "math/rand")func main(){
    cache:=make(chan int,4)    go func() {        for i:=0;i< 10;i++ {
            cache<-i
        }
    }()    go getCache(cache)    go getCache(cache)    go getCache(cache)
    time.Sleep(3*time.Second)
}func getCache(cache <-chan int)  {    for  {        select {        case i:=<-cache:            println(i)
            time.Sleep(time.Duration(rand.Int31n(100))*time.Millisecond)
        }
    }
}

多执行几次看看结果,并不是每一次都是可以顺序输出的,有缓存channel是乱序的。因为这里让一些同学误解了,我在此多解释一下。

针对通道的发送和接收操作都是可能造成相关的goroutine阻塞。

试想一下,有多个goroutine向同一个channel发送数据而被阻塞,如果还channel有多余的缓存空间时候,最早被阻塞的goroutine会最先被唤醒。

也就是说,这里的唤醒顺序与发送操作的开始顺序是一致的,对接收操作而言亦为如此。无论是发送还是接收操作,运行时系统每次只会唤醒一个goroutine。

而这里的乱序是指,如果像使用channel缓存中多个goroutine实现顺序是正确的,因为每一个goroutine抢到处理器的时间点不一致,所以不能保证顺序。

误解二:channel缓存的大小就是并发度

如下代码:

package mainimport ( "fmt"
 "sync"
 "time")var wg = sync.WaitGroup{}func main() {
 wg.Add(2)
 bf := make(chan string, 64) go insert(bf) go get(bf)
 wg.Wait()
}func insert(bf chan string) {
 str := "CockroachDB 的技术选型比较激进,比如依赖了 HLC 来做事务的时间戳。但是在 Spanner 的事务模型的 Commit Wait 阶段等待时间的选择,CockroachDB 并没有办法做到 10ms 内的延迟;CockroachDB 的 Commit Wait 需要用户自己指定,但是谁能拍胸脯说 NTP 的时钟误差在多少毫秒内?我个人认为在处理跨洲际机房时钟同步的问题上,基本只有硬件时钟一种办法。HLC 是没办法解决的。另外 Cockroach 采用了 gossip 来同步节点信息,当集群变得比较大的时候,gossip 心跳会是一个非常大的开销。当然 CockroachDB 的这些技术选择带来的优势就是非常好的易用性,所有逻辑都在一个 binary 中,开箱即用,这个是非常大的优点。"
 for i := 0; i < 10000000; i++ {
  bf <- fmt.Sprintf("%s%d", str, i)
 }
 wg.Done()
}func sprint(s string) {
 time.Sleep(1000 * time.Millisecond)
}func get(bf chan string) { for {  go func() {   select {   case str := <-bf:
    sprint(str)   case <-time.After(3 * time.Second):
    wg.Done()
   }
  }()
 }
}

很多同学乍一看以为定义了

bf := make(chan string, 64)

就是说该程序的并发度控制在了64,执行就会发现内存一直在增长。

因为get()函数中启动的goroutine会越来越多,因为get()每读取一个数据,insert()就会往channel插入一条数据,此时并发度就不是64了。

需要修改为:

package mainimport ( "fmt"
 "sync"
 "time")var wg = sync.WaitGroup{}func main() {
 wg.Add(2)
 bf := make(chan string, 64) go insert(bf) //go get(bf)
    for i:=0;i<64;i++ {        go get1(bf)
    }
 wg.Wait()
}func insert(bf chan string) {
 str := "CockroachDB 的技术选型比较激进,比如依赖了 HLC 来做事务的时间戳。但是在 Spanner 的事务模型的 Commit Wait 阶段等待时间的选择,CockroachDB 并没有办法做到 10ms 内的延迟;CockroachDB 的 Commit Wait 需要用户自己指定,但是谁能拍胸脯说 NTP 的时钟误差在多少毫秒内?我个人认为在处理跨洲际机房时钟同步的问题上,基本只有硬件时钟一种办法。HLC 是没办法解决的。另外 Cockroach 采用了 gossip 来同步节点信息,当集群变得比较大的时候,gossip 心跳会是一个非常大的开销。当然 CockroachDB 的这些技术选择带来的优势就是非常好的易用性,所有逻辑都在一个 binary 中,开箱即用,这个是非常大的优点。"
 for i := 0; i < 10000000; i++ {
  bf <- fmt.Sprintf("%s%d", str, i)
 }
 wg.Done()
}func sprint(s string) {
 time.Sleep(1000 * time.Millisecond)
}func get1(bf chan string)  {    for {        select {        case str := <-bf:
            sprint(str)        case <-time.After(3 * time.Second):
            wg.Done()
        }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
用golang如何替换某个文件中的字符串
Apr 25 Golang
golang http使用踩过的坑与填坑指南
Apr 27 Golang
golang 实现Location跳转方式
May 02 Golang
golang日志包logger的用法详解
May 05 Golang
go语言中GOPATH GOROOT的作用和设置方式
May 05 Golang
GoLang中生成UUID唯一标识的实现
May 08 Golang
Go语言实现一个简单的并发聊天室的项目实战
Mar 18 Golang
详解Golang如何优雅的终止一个服务
Mar 21 Golang
Go语言特点及基本数据类型使用详解
Mar 21 Golang
golang生成vcf通讯录格式文件详情
Mar 25 Golang
详解Go语言中配置文件使用与日志配置
Jun 01 Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 Golang
Go使用协程交替打印字符
Apr 29 #Golang
golang goroutine顺序输出方式
Apr 29 #Golang
golang 在windows中设置环境变量的操作
解决golang在import自己的包报错的问题
golang import自定义包方式
golang 接口嵌套实现复用的操作
Apr 29 #Golang
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 #Golang
You might like
福利彩票幸运号码自动生成器
2006/10/09 PHP
PHP下判断网址是否有效的代码
2011/10/08 PHP
php实例分享之html转为rtf格式
2014/06/02 PHP
php中error与exception的区别及应用
2014/07/28 PHP
php分割合并两个字符串的函数实例
2015/06/19 PHP
老生常谈PHP位运算的用途
2017/03/12 PHP
根据分辩率调用不同的CSS.
2007/01/08 Javascript
调试代码导致IE出错的避免方法
2014/04/04 Javascript
JS逆序遍历实现代码
2014/12/02 Javascript
AngularJS入门教程之 XMLHttpRequest实例讲解
2016/07/27 Javascript
微信小程序 image组件binderror使用例子与js中的onerror区别
2017/02/15 Javascript
js判断文件类型大小并给出提示的实现方法
2018/01/03 Javascript
vue使用$emit时,父组件无法监听到子组件的事件实例
2018/02/26 Javascript
Linux Centos7.2下安装nodejs&amp;npm配置全局路径的教程
2018/05/15 NodeJs
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
详解vuex之store拆分即多模块状态管理(modules)篇
2018/11/13 Javascript
Vue.js递归组件实现组织架构树和选人功能案例分析
2019/07/03 Javascript
基于vue、react实现倒计时效果
2019/08/26 Javascript
vue+elementui通用弹窗的实现(新增+编辑)
2021/01/07 Vue.js
[02:34]DOTA2亚洲邀请赛 BG战队出场宣传片
2015/03/09 DOTA
[02:00]DAC2018主宣传片——龙征四海,剑问东方
2018/03/20 DOTA
python清除指定目录内所有文件中script的方法
2015/06/30 Python
python爬虫 正则表达式使用技巧及爬取个人博客的实例讲解
2017/10/20 Python
python Pandas库基础分析之时间序列的处理详解
2019/07/13 Python
多版本python的pip 升级后, pip2 pip3 与python版本失配解决方法
2019/09/11 Python
python实现图片横向和纵向拼接
2020/03/05 Python
python 匿名函数与三元运算学习笔记
2020/10/23 Python
HTML5之WebGL 3D概述(下)—借助类库开发及框架介绍
2013/01/31 HTML / CSS
俄罗斯建筑和装饰材料在线商店:Stroilandia
2020/07/25 全球购物
你们项目是如何进行变更控制的
2015/08/26 面试题
致跳高运动员广播稿
2014/01/13 职场文书
校园元旦活动总结
2014/07/09 职场文书
房屋租赁授权委托书范本
2014/09/20 职场文书
2014年教研组工作总结
2014/11/26 职场文书
新农村建设指导员工作总结
2015/08/13 职场文书
关爱留守儿童主题班会
2015/08/13 职场文书