在知乎看到有人分享了几道笔试题,自己总结了一下其中与channel有关的题目。全部题目在这里:

https://zhuanlan.zhihu.com/p/35058068

题目

5、请找出下面代码的问题所在。

func main() {
ch := make(chan int, 1000)
// goroutine1
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
}() // goroutine2
go func() {
for {
a, ok := <-ch
if !ok {
fmt.Println("close")
return
}
fmt.Println("a: ", a)
}
}()
close(ch)
fmt.Println("ok")
time.Sleep(time.Second * 100)
}

8、请说出下面代码哪里写错了

func main() {
abc := make(chan int, 1000)
for i := 0; i < 10; i++ {
abc <- i
} // receiver goroutine
go func() {
for {
a := <-abc
fmt.Println("a: ", a)
}
}()
close(abc)
fmt.Println("close")
time.Sleep(time.Second * 100)
}

解释

首先要明确这两个要点:

  • 向一个已经关闭的channel发送数据是会抛panic的,但是从一个已关闭的channel接收数据并不会抛panic,而是得到channel数据类型的零值。
  • 同一个段程序每次启动后goruntine的调度不一定相同,所以goroutine的执行顺序有可能不一样。

先看第一题,代码运行时会抛出panic:

panic: send on closed channel

这是因为goroutine1 还没把i 发送给ch,ch 就在main函数中被close,当把i 发送到ch 时就抛出了panic。同理goroutine2 中从已关闭的ch 中接收数据时ok 会返回false,然后就return了。而且由于goroutine 的执行顺序不一样,输出close、ok、panic的顺序也会不一样,有兴趣的话可以自己跑几次。

那么如果想让程序正常输出应该怎么改呢,下面是其中一种方法:

func main() {
ch := make(chan int, 1000)
// goroutine1
go func() {
for i := 0; i < 10; i++ {
ch <- i
} // 只有一个goroutine向ch发送数据,可以考虑在这个goroutine中关闭。
close(ch)
}() // goroutine2
go func() {
for {
a, ok := <-ch
if !ok {
fmt.Println("close")
return
}
fmt.Println("a: ", a)
}
}() fmt.Println("ok")
time.Sleep(time.Second * 100)
}

再来看第二题,运行时输出完0—9后会一直输出0,直到sleep的时间结束。这是因为在把0—9发送给abc 后abc 就被关闭了,receiver goroutine 在接收完0—9后没有跳出for 循环,而是一直从被关闭的abc 中接收数据,所以接收到的是abc 的零值——0。

有一种最简单的办法可以改正这个程序:

func main() {
abc := make(chan int, 1000)
for i := 0; i < 10; i++ {
abc <- i
}
go func() {
// 使用for-range,abc被关闭后会自动退出for循环。
for a := range abc {
fmt.Println("a: ", a)
}
}()
close(abc)
fmt.Println("close")
time.Sleep(time.Second * 10)
}

总结

使用channel时,

  • 如果只有一个goroutine 向channel发送数据,可以在该goroutine 中close channel。当有多个goroutine 向channel 发送元素时close channel 的方法可以参考这篇文章:

    https://www.jianshu.com/p/d24dfbb33781

  • 用for-range 从channel 中接收数据代码简洁而且基本没有副作用。

扩展阅读

关于goroutine是如何调度的,可以参考这篇文章:

https://blog.csdn.net/liangzhiyang/article/details/52669851

References

http://colobu.com/2016/04/14/Golang-Channels/

从两道题看go channel的用法的更多相关文章

  1. 从一道题看线程安全--牛客网Java基础题

    从一道题看线程安全 Java中的线程安全是什么: 就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的 ...

  2. /*透明度设置的两种方式,以及hover的用法,fixed,(relative,absolute)这两个一起用*/

    <!DOCTYPE html> /*透明度设置的两种方式,以及hover的用法,fixed,(relative,absolute)这两个一起用*/ <html lang=" ...

  3. Golang | 简介channel常见用法,完成goroutin通信

    今天是golang专题的第14篇文章,大家可以点击上方的专辑回顾之前的内容. 今天我们来看看golang当中另一个很重要的概念--信道.我们之前介绍goroutine的时候曾经提过一个问题,当我们启动 ...

  4. 一文看懂ConstraintLayout的用法

    ConstraintLayout 相对于 RelativeLayout来说性能更好,布局上也更加灵活.在最新的Google Android开发文档中是推荐使用 ConstraintLayout的,下面 ...

  5. php封装协议的两道题

    这几天终于刷完了自己说是要刷完的那几道题,赶紧写几篇博客记录.. 1.  先看看这个网站:https://blog.csdn.net/qq_41289254/article/details/81388 ...

  6. 从一道题看js的拆箱操作

    前段时间看到一道题,如下:([][[]]+[])[+![]]+([]+{})[!+[]+![]]问最终打印结果,然后简单了解一下js的装箱,拆箱操作. 基本 装箱操作: 就是将基本类型(String, ...

  7. 最近这两天看了关于H5游戏开发的一个教程,实践很短暂,看了很多理论的东西,现在呢也只是想回忆回忆关于EUI的部分知识吧

    首先我了解了什么是Egret: Egret中文就是白鹭的意思,Egret是一套H5游戏开发的软件.(纯粹属于个人理解) 其次我对以下几款软件的相关知识做了些了解: Egret Engine(引擎),E ...

  8. Codeforces 460 DE 两道题

    D Little Victor and Set 题目链接 构造的好题.表示是看了题解才会做的. 假如[l,r]长度不超过4,直接暴力就行了. 假如[l,r]长度大于等于5,那么如果k = 1,显然答案 ...

  9. js的call() ,apply() 两种方法的区别和用法,最白话文的解释,让枯燥滚粗!

    百度了一圈calll()函数和apply()函数,感觉还是糊里糊涂 正好我前几天刚又重新翻了一遍 那本 600多页 的圣经书,我习惯时不时的去打下基础,只是为了用来装逼,给人讲解....(我是有多蛋疼 ...

随机推荐

  1. ip地址查询系统和CMD查询的结果不一样

    由于cmd输入 ipconfig查看的IP是局域网内网IP,而用ip地址查看器查看是公网上网的ip地址.所以不一样. 查询内网ip: windows系统: 开始--运行--cmd,命令行输入: ipc ...

  2. phpstrom如何定义文件打开的方式

    今天想vue结合PHP来小写一段代码,但是发现自己把vue的文件放进去,会显示text文本,在刚开始的时候,编辑器会提示我们以什么格式打开,我没在意的选择了text,结果悲催了,那么在设置里面的哪个选 ...

  3. 更改hadoop native库文件后datanode故障

    hadoop是用cloudra的官方yum源安装的,服务器是CentOS6.3 64位操作系统,自己写的mapreduce执行的时候hadoop会提示以下错误: WARN util.NativeCod ...

  4. golang 之 channel

    channel的机制是先进先出 无缓冲的channel: 如果你给channel赋值了,那么必须要读取它的值,不然就会造成阻塞. chreadandwrite :=make(chan int) cho ...

  5. 【BZOJ1895】Pku3580 supermemo Splay

    [BZOJ1895]Pku3580 supermemo Description 给出一个初始序列fA1;A2;:::Ang,要求你编写程序支持如下操作: 1. ADDxyD:给子序列fAx:::Ayg ...

  6. LeetCode 笔记系列12 Trapping Rain Water [复杂的代码是错误的代码]

    题目:Given n non-negative integers representing an elevation map where the width of each bar is 1, com ...

  7. (转)MySQL百万级数据库优化

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  8. 160406、mybatis批量删除

    <deleteid="deleteCTQ" parameterType="java.lang.String"> DELETE FROM sqm_pr ...

  9. puppeteer部署到centOS上出现launch chrome fail的情况

    在Mac上调试无问题,放到阿里云上运行会报错. 需要先安装依赖, yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 lib ...

  10. 前端开发 - JQuery - 上

    一.js的缺点 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...