本节紧接上一节的内容,主要介绍channel中的有缓冲channel、单方向的channel、定时器,以及select语句在channel中的应用。
3.4 有缓冲的channel
3.4.1 概述
有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个值的通道。这种类型的通道并不强制要求goroutine之间必须同时完成发送和接收,通道会阻塞发送和接收动作的条件也不同:只有在通道中没有要接收的值时,接收动作才会阻塞;只有在通道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞。有缓冲的通道和无缓冲的通道之间的最大不同在于:无缓冲通道保证发送和接收的goroutine会在同一时间进行数据交换;有缓冲的通道没有这种保证。图2通过示意图分析了多个goroutine利用有缓冲通道来共享一个值:
a. 第1步,右侧的goroutine正在从通道中接收一个值;
b. 第2步,右侧的这个goroutine独立完成了接收值的动作,而左侧的goroutine正在发送一个新值到通道里;
c. 第3步,左侧的goroutine向通道发送新值,右侧的goroutine正在从通道接收另外一个值。这个步骤里的两个操作既不是同步的,也不会互相阻塞;
d. 第4步,所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存更多的值。
3.4.2 有缓冲的channel的创建
如果给定了一个缓冲区容量,通道就是异步的。只要缓冲区有未使用空间用于发送数据,或者还包含可以接收的数据,那么其通信就会无阻塞地进行。有缓冲的channel创建格式如下:
示例代码:
3.5 单方向的channel
3.5.1 概述
channel默认双向。既可以向channel里发送数据,也可以从channel里接收数据。但是,要使channel作为参数传递且被单向使用,即只向该channel发送数据,或此channel只接收数据,那么需要对此channel指定方向。
3.5.2 单向channel变量的声明
单向channel变量有两种:一种是只能向channel里发送数据的变量,另一种是只能从channel里接收数据的变量。单向channel变量的声明格式如表2。
注:
a. chan<-表示把数据写入管道;
b. <-chan表示将数据从管道中读取。
3.5.3 channel的转换
普通的channel可以隐式地转换为单向的channel,只接收数据或只发送数据;但是单向的channel不能转换为普通的channel。
示例代码:
四、select
4.1 select语法结构
Go语言关键字select可以监听channel上的数据流动。它的用法与switch语句类似,由select开始一个新的选择块,每个选择条件由case语句来描述。与switch语句可以选择任何可使用相等比较的条件相比, select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO(输入/输出)操作,它的语法结构如下:
4.2 select语句执行顺序
在select语句中,Go语言按顺序从头至尾评估每一个发送和接收的语句:
如果其中任意一条语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
如果没有一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:第一,如果有default语句,那么就会执行default语句,同时程序的执行会从select语句后的语句中恢复;第二,如果没有default语句,那么select语句将被阻塞,直到至少有一个case语句可以进行下去。
在没有case准备就绪时,可以执行select语句中的default语句,这通常用于防止select语句一直阻塞。
4.3 在select语句中设置超时
当goroutine阻塞时,为避免整个程序进入阻塞,可以在select语句设置超时,具体通过如下方式实现:
示例如下:
参考资料:
https://www.cnblogs.com/tangchuanyang/p/5553434.html
http://www.flysnow.org/2017/04/11/go-in-action-go-goroutine.html
https://studygolang.com/articles/3028