channel[通道]是golang的一种重要特性,正是因为channel的存在才使得golang不同于其它语言。channel使得并发编程变得简单容易有趣。

channel的概念和语法

一个channel可以理解为一个先进先出的消息队列。channel用来在协程[goroutine]之前传递数据,准确的说,是用来传递数据的所有权。一个设计良好的程序应该确保同一时刻channel里面的数据只会被同一个协程拥有,这样就可以避免并发带来的数据不安全问题[data races]。

channel的类型

像数组、切片和字典一样,channel类型是一种组合类型,每一种channel类型都对应着一种简单的数据类型。比如元素的类型是string,那么对应的channel类型就是chan string,进入channel的数据也就必须是string类型的值。

官方的go编译器限制channel里的单个元素最多65535个字节,也就是说如果channel缓冲数组里面容纳的是struct,那这个struct的size不能大过65535。尽管如此,我们也不应该传递体积过大的元素值,因为channel的数据从进入到流出会涉及到数据拷贝操作。如果元素体积过大,最好的方法还是使用传递指针来取代传递值。

channel类型是可以带有方向的,假设T是一种类型

  1. chan T是双向channel类型,编译器允许对双向channel同时进行发送和接收。
  2. chan<- T是只写channel类型,编译器只允许往channel里面发送数据。
  3. <-chan T是只读channel类型,编辑器只允许从channel里面接收数据。

双向类型的channel,可以被强制转换成只读channel或者是只写channel,但是反过来却不行,只读和只写channel是不可以转换成双向channel的。

channel类型的零值形式称为空channel。一个非空channel类型必须通过make关键字进行创建。例如make(chan int, 10)将会创建出一个可以容纳10个int值的channel。第二个整形的参数值代表的就是channel可以容纳数据的大小,如果不提供这个参数值,那默认值就是零。

var ch chan string; // nil channel
ch := make(chan string); // zero channel
ch := make(chan string, 10); // buffered channel

channel里面的value buffer的容量也就是channel的容量。channel的容量为零表示这是一个阻塞型通道,非零表示缓冲型通道[非阻塞型通道]。

channel内部结构

每个channel内部实现都有三个队列

  1. 接收消息的协程队列。这个队列的结构是一个限定最大长度的链表,所有阻塞在channel的接收操作的协程都会被放在这个队列里。
  2. 发送消息的协程队列。这个队列的结构也是一个限定最大长度的链表。所有阻塞在channel的发送操作的协程也都会被放在这个队列里。
  3. 环形数据缓冲队列。这个环形数组的大小就是channel的容量。如果数组装满了,就表示channel满了,如果数组里一个值也没有,就表示channel是空的。对于一个阻塞型channel来说,它总是同时处于即满又空的状态。

一个channel被所有使用它的协程所引用,也就是说,只要这两个装了协程的队列长度大于零,那么这个channel就永远不会被垃圾回收。另外,协程本身如果阻塞在channel的读写操作上,这个协程也永远不会被垃圾回收,即使这个channel只会被这一个协程所引用。

channel的使用

channel支持以下操作

  1. 使用cap(ch)函数查询channel的容量,cap是golang的内置函数
  2. 使用len(ch)函数查询channel内部的数据长度,len函数也是内置的,表面上这个函数很有意义,但实际上它很少用。
  3. 使用close(ch)关闭channel,close也是内置函数。一个非空channel只能够被关闭一次,如果关闭一个已经被关闭的或者是关闭一个空channel将会引发panic。另外关闭一个只读channel是非法的,编译器直接报错。
  4. 使用ch <- v发送一个值v到channel。发送值到channel可能会有多种结果,即可能成功,也可能阻塞,甚至还会引发panic,取决于当前channel在什么状态。
  5. 使用 v, ok <- ch 接收一个值。第二个遍历ok是可选的,它表示channel是否已关闭。接收值只会又两种结果,要么成功要么阻塞,而永远也不会引发panic。

所有的这些操作都是同步的协程安全的,不需要加任何其它同步控制。

For-Range

for-range语法可以用到通道上。循环会一直接收channel里面的数据,直到channel关闭。不同于array/slice/map上的for-range,channel的for-range只允许有一个变量。

for v = range aChannel {// use v
}

等价于

for {v, ok = <-aChannelif !ok {break}// use v
}

注意,for-range对应的channel不能是只写channel。

Select-Cases

select块是为channel特殊设计的语法,它和switch语法非常相近。分支上它们都可以有多个case块和做多一个default块,但是也有很多不同

  1. select 到 括号{之间不得有任何表达式
  2. fallthrough关键字不能用在select里面
  3. 所有的case语句要么是channel的发送操作,要么就是channel的接收操作
  4. select里面的case语句是随机执行的,而不能是顺序执行的。设想如果第一个case语句对应的channel是非阻塞的话,case语句的顺序执行会导致后续的case语句一直得不到执行除非第一个case语句对应的channel里面的值都耗尽了。
  5. 如果所有case语句关联的操作都是阻塞的,default分支就会被执行。如果没有default分支,当前goroutine就会阻塞,当前的goroutine会挂接到所有关联的channel内部的协程队列上。 所以说单个goroutine是可以同时挂接到多个channel上的,甚至可以同时挂接到同一个channel的发送协程队列和接收协程队列上。当一个阻塞的goroutine拿到了数据接触阻塞的时候,它会从所有相关的channel队列中移除掉。

channel简单规则表

下标的活跃Channel表示即非空又非关闭的Channel

channel规则详细解释

空channel

  1. 关闭一个空channel会导致当前goroutine引发panic
  2. 向一个空channel发送值会导致当前的goroutine阻塞
  3. 从一个空channel接收值也会导致当前的goroutine阻塞

在空channel上的调用len和cap函数都统一返回零。

已关闭的Channel

  1. 关闭一个已关闭的channel会引发panic
  2. 向一个已关闭的channel发送值会引发panic。当这种send操作处于select块里面的case语句上时,它会随时导致select语句引发panic。
  3. 从一个已关闭的channel上接收值既不会阻塞也不能panic,它一直能成功返回。只是返回的第二个值ok永远是false,表示接收到的v是在channel关闭之后拿到的,对应得值也是相应元素类型的零值。可以无限循环从已关闭的channel上接收值。

活跃的Channel

  1. 关闭操作

    1. 从channel的接收协程队列中移除所有的goroutine,并唤醒它们。
    2. 从channel的接收协程队列中移除所有的goroutine,并唤醒它们。
    3. 一个已关闭的channel内部的缓冲数组可能不是空的,没有接收的这些值会导致channel对象永远不会被垃圾回收。
  2. 发送操作
    1. 如果是阻塞型channel,那就从channel的接收协程队列中移出第一个协程,然后把发送的值直接递给这个协程。
    2. 如果是阻塞型channel,并且channel的接收协程队列是空的,那么当前的协程将会阻塞,并进入到channel的发送协程队列里。
    3. 如果是缓冲型channel,并且缓冲数组里还有空间,那么将发送的值添加到数组最后,当前协程不阻塞。
    4. 如果是缓冲型channel,并且缓冲数组已经满了,那么当前的协程将会阻塞,并进入到channel的发送协程队列中。
  3. 接收操作
    1. 如果是缓冲型channel,并且缓冲数组有值,那么当前的协程不会阻塞,直接从数组中拿出第一个值。如果发送队列非空,还需要将队列中的第一个goroutine唤醒。
    2. 如果是阻塞型channel,并且发送队列非空的话,那么唤醒发送队列第一个协程,该协程会将发送的值直接递给接收的协程。
    3. 如果是缓冲型channel,并且缓冲数组为空,或者是阻塞型channel,并且发送协程队列为空,那么当前协程将会阻塞,并加入到channel的接收协程队列中。

总结

根据以上规则,我们可以得出以下结论

  1. 如果channel关闭了,那么它的接收和发送协程队列必然空了,但是它的缓冲数组可能还没有空。
  2. channel的接收协程队列和缓冲数组,同一个时间必然有一个是空的
  3. channel的缓冲数组如果未满,那么它的发送协程队列必然是空的
  4. 对于缓冲型channel,同一时间它的接收和发送协程队列,必然有一个是空的
  5. 对于非缓冲型channel,一般来说同一时间它的接收和发送协程队列,也必然有一个是空的,但是有一个例外,那就是当它的发送操作和接收操作在同一个select块里出现的时候,两个队列都不是空的。

各种使用的结论:

1.没有初始化的channel 如Var c channel,可以往里面扔东西吗? 不行 扔东西和取东西都是阻塞的
2.两次close channel会发生什么?      【panic】
3.关闭的channel可以往里面发消息吗?    不行,【panic】
4.如果关闭空channel  如:var ch chan int   close(ch)  ?     【panic】
5.给已关闭的通道发送数据  ?    panic: send on closed channel
6.如果在有缓存的channel里写数据,关闭channel,会怎样?   程序正常运行   那里面的数据可不可以读出来呢?  可以读出来
7.如果是没有缓存的channel里写数据,关闭channel,会这样?  error

channel基本介绍狗朗相关推荐

  1. Golang之Channel详细介绍

    一.概述 通道(Channel)是 Golang 在语言级别上提供的 goroutine 间的通讯方式,可以使用channel在多个 goroutine 之间传递消息.如果说 goroutine 是 ...

  2. Channel基本介绍

    基本介绍 1.NIO的通道类(Channel)似于流,或者理解为一个连接,但有些区别如下 通道可以同时进行读写,而流只能读或者只能写 通道可以实现异步读写数据 通道可以从缓冲读数据,也可以写数据到缓冲 ...

  3. 【Simulink 仿真】SISO Fading Channel模块介绍

    目录 1 简介 2 Rayleigh Fading channel参数 3 Rician Fading channel参数 4 可视化选项设置 6 MATLAB代码实现 1 简介 SISO衰落通道模块 ...

  4. NIO 学习(三) channel(主要介绍channel----FileChannel详解--通道间的信息传输)

    Channel实现 Java NIO中最重要的通道的实现: FileChannel :从文件中读写数据 DatagramChannel :通过UDP读写网络中的数据 SocketChannel  :通 ...

  5. Pytorch+CNN+猫狗分类实战

    文章目录 0.前言 1.猫狗分类数据集 1.1数据集下载(可选部分) 1.2数据集分析 2.猫狗分类数据集预处理 2.1训练集和测试集划分 2.2训练集和测试集读取 3.剩余代码 4.总结 0.前言 ...

  6. Golang管道channel:管道的声明 读取 写入

    为什么需要channel channel的介绍 管道的本质是队列 FIFO 先进先出 定义/声明管道 channel的初始化和写数据 管道的本质 向管道写入数据及查看管道的长度和容量 往管道加数据,不 ...

  7. go channel 缓冲区最大限制_[Go区块链基础]go channel

    channel是Go语言在语言级别提供的goroutine间的通信方式.我们可以使用channel在两个或多个goroutine之间传递消息. channel是进程内的通信方式,因此通过channel ...

  8. 玩转Kaggle:Dog Breed Identification【识别狗的类别】

    文章目录 1. Kaggle数据集介绍 2. 下载数据和数据整理(kaggle官网即可) 3. 图像增广 4. 加载数据 5.微调模型 6.可视化validation数据 7. 输出测试结果并提交ka ...

  9. ESP8266 PMW介绍

    前言 呼吸灯是常见的LED应用.LED呼吸灯的流程是缓缓点亮LED灯,再缓缓熄灭LED灯.在夜色下,缓慢闪烁的LED显得格外迷人. 一.PWM单元介绍 ESP8266有四个PWM输出接口,分别是:PW ...

最新文章

  1. 机器学习虽好,也要看什么场合
  2. 腐蚀rust电脑分辨率调多少_腐蚀RUST提高FPS教程 腐蚀RUST如何提高FPS
  3. AJPFX学习笔记JavaAPI之String类
  4. hs300 quant
  5. 计算机技能鉴定几月考,计算机等级考试和职业技能鉴定考核大纲(二级Office).docx...
  6. 第九集 生死穿越风火山,感受尘世间轮回
  7. leetcode39 --- combinationSum
  8. 深刻理解HDFS工作机制
  9. 力扣 数组的相对排序
  10. 网络编程基础知识之单线程与多线程
  11. mysql insert or update_MySQL insert or update SQL 示例
  12. STVP烧录出现Verify failed at address 0x1000
  13. 苹果电脑访问文件共享服务器,mac电脑肿么访问mac共享的文件
  14. linux wine 原理,wine的工作原理与自动运行PE程序
  15. c语言在内存存储结构,C语言中float,double类型,在内存中的结构(存储方式)
  16. PYTHON利用REMOVEBG库实现抠图
  17. Oracle触发器,删除一条数据的同时删除另一张表的关联数据
  18. 170323 PyQt5 ListWidget的删除
  19. 埃里克贝里奇_未来公司客服的标配?苹果Watch智能手表开启新时代
  20. nginx:代理服务器(涉及upstream)的例子

热门文章

  1. JAVA 基础——学习
  2. python将一个列表赋值给另一个列表_将一个列表分配给另一个
  3. java基于微信小程序旅游管理系统 uniapp 小程序
  4. kettle/Pentaho工具之阻塞数据直到步骤都完成
  5. 咸鱼的白日梦(疯~的美丽日记)
  6. JavaScript冷门知识
  7. php exit code 255,使用exit(-1)为什么得到255退出码?
  8. ROS机器人程序设计(原书第2版)学习镜像分享及使用说明
  9. windows-运维-03 活动目录
  10. WPS 查找历史版本记录