使用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判断key是否在map中的代码
Apr 24 Golang
Go语言使用select{}阻塞main函数介绍
Apr 25 Golang
解决Goland 同一个package中函数互相调用的问题
May 06 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
go语言中http超时引发的事故解决
Jun 02 Golang
Go 语言下基于Redis分布式锁的实现方式
Jun 28 Golang
Go语言空白表示符_的实例用法
Jul 04 Golang
基于Go语言构建RESTful API服务
Jul 25 Golang
Go语言基础函数基本用法及示例详解
Nov 17 Golang
Golang使用Panic与Recover进行错误捕获
Mar 22 Golang
golang三种设计模式之简单工厂、方法工厂和抽象工厂
Apr 10 Golang
Go web入门Go pongo2模板引擎
May 20 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
一步一步学习PHP(4) php 函数 补充2
2010/02/15 PHP
防止本地用户用fsockopen DDOS攻击对策
2011/11/02 PHP
PHP批量采集下载美女图片的实现代码
2013/06/03 PHP
php定义数组和使用示例(php数组的定义方法)
2014/03/29 PHP
php三种实现多线程类似的方法
2015/10/30 PHP
详解PHP处理字符串类似indexof的方法函数
2017/06/11 PHP
详解php框架Yaf路由重写
2017/06/20 PHP
PHP校验15位和18位身份证号的类封装
2018/11/07 PHP
toggle()隐藏问题的解决方法
2014/02/17 Javascript
详细谈谈AngularJS的子级作用域问题
2016/09/05 Javascript
jQuery获取this当前对象子元素对象的方法
2016/11/29 Javascript
javascript实现Java中的Map对象功能的实例详解
2017/08/21 Javascript
详解各版本React路由的跳转的方法
2018/05/10 Javascript
js自定义input文件上传样式
2018/10/26 Javascript
Jquery获取radio选中值实例总结
2019/01/17 jQuery
详解vue中的父子传值双向绑定及数据更新问题
2019/06/13 Javascript
layui点击左侧导航栏,实现不刷新整个页面,只刷新局部的方法
2019/09/25 Javascript
从0搭建vue-cli4脚手架
2020/06/17 Javascript
[07:12]2014DOTA2西雅图国际邀请赛 黑马Liquid专题采访
2014/07/12 DOTA
[48:27]EG vs Liquid 2018国际邀请赛淘汰赛BO3 第二场 8.25
2018/08/29 DOTA
python实现探测socket和web服务示例
2014/03/28 Python
在Python中使用PIL模块处理图像的教程
2015/04/29 Python
Python编程使用NLTK进行自然语言处理详解
2017/11/16 Python
Python爬取当当、京东、亚马逊图书信息代码实例
2017/12/09 Python
使用python 爬虫抓站的一些技巧总结
2018/01/10 Python
Django 反向生成url实例详解
2019/07/30 Python
Django中自定义模型管理器(Manager)及方法
2019/09/23 Python
python主线程与子线程的结束顺序实例解析
2019/12/17 Python
django model的update时auto_now不被更新的原因及解决方式
2020/04/01 Python
HTML5对比HTML4的主要改变和改进总结
2016/05/27 HTML / CSS
Giglio德国网上精品店:奢侈品服装和配件
2016/09/23 全球购物
一些Solaris面试题
2013/03/22 面试题
就业推荐表自我鉴定
2013/10/29 职场文书
竞聘上岗演讲稿范文
2014/01/10 职场文书
2015年政治教研组工作总结
2015/07/22 职场文书
2016师德师风学习心得体会
2016/01/12 职场文书