Golang 中的 channel 有两种类型,分别为:无缓冲 channel 和 带缓冲 channel。
无缓冲的通道是指在接收前没有能力保存任何值的通道。这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。
带缓冲的通道是一种在被接收前能存储一个或者多个值的通道。这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。
带通道会阻塞发送和接收动作的条件也会不同。只有在通道中没有要接收的值时,接收动作才会阻塞。只有在通道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞。
无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换,而有缓冲的通道没有这种保证。
在无缓冲通道的基础上,为通道增加一个有限大小的存储空间形成带缓冲通道。带缓冲通道在发送时无需等待接收方接收即可完成发送过程,并且不会发生阻塞,只有当存储空间满时才会发生阻塞。同理,如果缓冲通道中有数据,接收时将不会发生阻塞,直到通道中没有数据可读时,通道将会再度阻塞。
无缓冲通道保证收发过程同步。而无缓冲是异步的收发过程,因此效率可以有明显的提升。
c1:= make(chan TYPE, bufferSize)
参数 | 描述 |
---|---|
c1 | channel 变量名。 |
chan | 创建 channel 使用的关键字。 |
TYPE | channel 的类型。 |
bufferSize | channel 的缓冲区大小。 |
我们创建了一个带缓冲的 channel c1,其类型为 chan TYPE,缓冲区大小为 bufferSize。
使用带缓冲 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)
}
程序运行后,控制台输出如下:
首先,我们使用 make 创建了一个带缓冲的 channel,其缓冲区大小为 3,接着,分别创建了一个负责写入数据的协程 writeRoutine 和一个负责读取数据的协程 readRoutine。
在 writeRoutine 协程里,我们使用 for 循环 发送三次数据,每次发送数据后,都使用 sleep 等待两秒,数据全部发送完毕,使用 close 关闭 channel。
在 readRoutine 函数里,使用 for range 接受数据,每次接受完数据后,都等待 10 秒钟。接受数据的时长明显长于发送数据的时长。
因为,发送数据的时间间隔明显小于接受数据的时间间隔,如果我们使用的是无缓冲的 channel,那么此时接受和发送是同步的,必须是接受完才能发送下一个。
但,我们这里创建的是无缓冲的 channel,因此,发送数据端不必等待数据被接受,就可以继续下一次发送,除非缓冲区满,才会阻塞,因此,我们看到的收发数据的效果是异步的。
带缓冲的通道是一种在被接收前能存储一个或者多个值的通道。这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。Go 语言带缓冲 channel 创建:
c1:= make(chan int, bufferSize)