在知乎看到有人分享了几道笔试题,自己总结了一下其中与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. hive经常使用命令

    hive经常使用命令 show tables; 列出hive里面全部数据表名 desc userProfile; 显示数据表userProfile的基本表字段及字段type desc extended ...

  2. HashMap实现原理、核心概念、关键问题的总结

    简单罗列一下较为重要的点: 同步的问题 碰撞处理问题 rehash的过程 put和get的处理过程 HashMap基础: HashMap的理论基础:维基百科哈希表 JDK中HashMap的描述:Has ...

  3. UILabel 行间距设置

    NSMutableAttributedString * attributedString1 = [[NSMutableAttributedString alloc] initWithString:te ...

  4. hdu1244(dp)

    简单dp Max Sum Plus Plus Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  5. Android 通过findViewById方式创建TabHost

    package org.shuxiang.tabhostsample; import android.os.Bundle; import android.app.ActivityGroup; impo ...

  6. 用angular引入复杂的json文件2

    昨天我们也说了一下angular引入复杂json文件的方法,今天我们再来学习一种方法,而且更简单,更快捷. 首先我们引入一个angular插件,并且写上引入模块和控制台,在html中书写上模块名和控制 ...

  7. 1. Action 实现 ModelDriven 接口后的运行流程

    1). 先会执行 ModelDrivenInterceptor 的 intercept 方法. public String intercept(ActionInvocation invocation) ...

  8. 20165330 2017-2018-2 《Java程序设计》第3周学习总结

    课本知识总结 第四章 类与对象 类:包括类声明和类体 基本格式: class 类名 { 类体的内容 } 类声明: class+类名(注意:类名首字母需大写) 类体:类声明之后的一对"{&qu ...

  9. Oracle 数存储——物理结构

    Oracle存储——逻辑结构 Oracle 数据库存储物理结构 物理存储结构是实际的数据存储单元,对应于操作系统文件. oracle数据库就是由驻留在服务器的磁盘上的这些操作系统文件组成的. 物理存储 ...

  10. 关于ios7 UINavigationController.interactivePopGestureRecognizer手势集成

    因为公司业务需求,结合网上的资料整理了一下. 如果自定义过navbar的leftbarbutton 或者backbarbutton 原生interactivePopGestureRecognizer默 ...