Go语言带缓冲channel

Go语言带缓冲channel教程

Golang 中的 channel 有两种类型,分别为:无缓冲 channel 和 带缓冲 channel。

无缓冲的通道是指在接收前没有能力保存任何值的通道。这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。

带缓冲的通道是一种在被接收前能存储一个或者多个值的通道。这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。

带通道会阻塞发送和接收动作的条件也会不同。只有在通道中没有要接收的值时,接收动作才会阻塞。只有在通道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞。

Go语言无缓冲通道和带缓冲通道区别

无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换,而有缓冲的通道没有这种保证。

在无缓冲通道的基础上,为通道增加一个有限大小的存储空间形成带缓冲通道。带缓冲通道在发送时无需等待接收方接收即可完成发送过程,并且不会发生阻塞,只有当存储空间满时才会发生阻塞。同理,如果缓冲通道中有数据,接收时将不会发生阻塞,直到通道中没有数据可读时,通道将会再度阻塞。

无缓冲通道保证收发过程同步。而无缓冲是异步的收发过程,因此效率可以有明显的提升。

带缓冲channel创建

语法

c1:= make(chan TYPE, bufferSize)

参数

参数 描述
c1 channel 变量名。
chan 创建 channel 使用的关键字。
TYPE channel 的类型。
bufferSize channel 的缓冲区大小。

说明

我们创建了一个带缓冲的 channel c1,其类型为 chan TYPE,缓冲区大小为 bufferSize。

案例

带缓冲channel读写数据

使用带缓冲 channel 读写数据

package main import ( "fmt" "time" ) //使用带缓冲 channel 发送数据 func writeRoutine(intChan chan int) { for i := 0; i < 3; i++ { intChan <- i time.Sleep(time.Duration(2) * time.Second) fmt.Println("Send", i) } //关闭 channel close(intChan) } // 从带缓冲 channel 读取数据 func readRoutine(intChan chan int) { for val := range intChan{ fmt.Println("Receive =", val) time.Sleep(time.Duration(10) * time.Second) } return } func main() { fmt.Println("嗨客网(www.haicoder.net)") // 创建带缓冲 channel c := make(chan int, 3) go writeRoutine(c) readRoutine(c) }

程序运行后,控制台输出如下:

26_golang带缓冲channel.png

首先,我们使用 make 创建了一个带缓冲的 channel,其缓冲区大小为 3,接着,分别创建了一个负责写入数据的协程 writeRoutine 和一个负责读取数据的协程 readRoutine。

在 writeRoutine 协程里,我们使用 for 循环 发送三次数据,每次发送数据后,都使用 sleep 等待两秒,数据全部发送完毕,使用 close 关闭 channel。

在 readRoutine 函数里,使用 for range 接受数据,每次接受完数据后,都等待 10 秒钟。接受数据的时长明显长于发送数据的时长。

因为,发送数据的时间间隔明显小于接受数据的时间间隔,如果我们使用的是无缓冲的 channel,那么此时接受和发送是同步的,必须是接受完才能发送下一个。

但,我们这里创建的是无缓冲的 channel,因此,发送数据端不必等待数据被接受,就可以继续下一次发送,除非缓冲区满,才会阻塞,因此,我们看到的收发数据的效果是异步的。

Go语言带缓冲channel总结

带缓冲的通道是一种在被接收前能存储一个或者多个值的通道。这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。Go 语言带缓冲 channel 创建:

c1:= make(chan int, bufferSize)