Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。
如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。
Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。
创建 channel
channel是一种类型,一种引用类型,道类型的空值是nil。声明通道类型的格式如下:
var 变量 chan 元素类型
要对通道进行操作,需要使用make操作符,make会初始化通道,在内存中分配通道的空间。
var c = make(chan int)
通道操作
发送
将一个值发送到通道中。
ch <- 10 // 把10发送到ch中
接收
从一个通道中接收值。
x := <- ch // 从ch中接收值并赋值给变量x
<-ch // 从ch中接收值,忽略结果
关闭
我们通过调用内置的close函数来关闭通道。
close(ch)
channel类型
无缓冲channel
是指在接收前没有能力保存任何值得通道。
c := make(chan int) // 创建一个无缓冲(unbuffered)的int类型的channe
由于无缓冲channel的运行时层实现不带有缓冲区,因此对无缓冲channel的接收和发送操作是同步的,即对于同一个无缓冲channel,只有在对其进行接收操作的goroutine和对其进行发送操作的goroutine都存在的情况下,通信才能进行,否则单方面的操作会让对应的goroutine陷入阻塞状态。
有缓冲channel
指通道可以保存多个值。
c := make(chan T, capacity) // T为channel中元素的类型, capacity为带缓冲channel的缓冲区容量
由于带缓冲channel的运行时层实现带有缓冲区,因此对带缓冲channel的发送操作在缓冲区未满、接收操作在缓冲区非空的情况下是异步 的(发送或接收无须阻塞等待)。也就是说,对一个带缓冲channel,在缓冲区无数据或有数据但未满的情况下,对其进行发送操作的goroutine不会阻塞;在缓冲区已满的情况下,对其进行发送操作的goroutine会阻塞;在缓冲区为空的情况下,对其进行接收操作的goroutine亦会阻塞
单方向channel
当一个channel作为一个函数参数时,它一般总是被专门用于只发送或者只接收。
为了表明这种意图并防止被滥用,Go语言的类型系统提供了单方向的channel类型,分别用于只发送或只接收的channel。类型chan<- int表示一个只发送int的channel,只能发送不能接收。相反,类型<-chan int表示一个只接收int的channel,只能接收不能发送。(箭头<-和关键字chan的相对位置表明了channel的方向。)这种限制将在编译期检测。
func counter(out chan<- int) {
for i := 0; i < 100; i++ {
out <- i
}
close(out)
}
func squarer(out chan<- int, in <-chan int) {
for i := range in {
out <- i * i
}
close(out)
}
func printer(in <-chan int) {
for i := range in {
fmt.Println(i)
}
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go counter(ch1)
go squarer(ch2, ch1)
printer(ch2)
}
参考资料
- 《Go 语言第一课》
- 《Go语言底层原理剖析》
- 《Go语言精进之路》
- 《Go语言圣经》