Go 语言通道(channel)
上一节我们介绍了协程(goroutine)的概念和用法,但是单纯地将函数并发执行是没有意义的,函数与函数间需要交换数据才能体现并发执行函数的意义。Go 语言为 goroutine 间的数据交换提供了一种简单的方法 —— 通道(channel)。
Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出的规则,保证收发数据的顺序。Go 的每一个通道都是一个具体类型的导管,因此声明 channel 的时候需要为其指定元素类型。同时,通道还提供了同步机制,确保数据在发送和接收之间的安全传递,可以有效地避免竞态条件和死锁等并发问题。
提示
本文所有示例代码可在 GitHub 下载。
通道的创建
声明通道类型变量需要使用 chan
关键字,语法格式如下:
var 变量名 chan 元素类型
注意:声明通道时需要指定元素类型,即通道中传递的元素的类型。
例如:
var a chan int // 声明一个传递 int 类型数据的通道
var b chan string // 声明一个传递 string 类型数据的通道
var c chan bool // 声明一个传递 bool 类型数据的通道
初始化通道
未经初始化的通道默认值为 nil
,需要使用 make()
函数初始化之后才能使用,语法格式如下:
make(chan 数据类型, [缓冲大小])
其中 channel 的缓冲大小是可选的。
例如:
ch := make(chan int) // 创建一个传递 int 类型数据的通道
ch := make(chan int) // 创建一个传递 int 类型数据、缓冲大小为10的通道
通道的操作
通道共有发送、接收、关闭三种操作,而发送和接收操作均用 <-
符号,请看下面几个例子。
-
声明通道并初始化
ch := make(chan int) // 声明一个通道并初始化
-
给一个通道发送值
ch <- 10 // 把10发送给ch通道
-
从一个通道中取值
x := <-ch // x从ch通 道中取值
<-ch // 从ch通道中取值,忽略结果 -
关闭通道
close(ch) // 关闭通道
注意:一个通道值是可以被垃圾回收掉的。
通道通常由发送方执行关闭操作,并且只有在接收方明确等待通道关闭的信号时才需要执行关闭操作。它和关闭文件不一样,通常在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。
关闭后的通道有以下特点:
- 对一个关闭的通道再发送值就会导致 panic。
- 对一个关闭的通道进行接收会一直获取值直到通道为空。
- 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
- 关闭一个已经关闭的通道会导致 panic。
无缓冲的通道
无缓冲的通道又称为阻塞的通道,我们来看一下如下代码片段:
channel_example_01.go
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 100
fmt.Println("发送成功")
}
上面这段代码能够通过编译,但是执行时会报错:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/rudy/workspace/go-courses/basic/goroutine/channel_example_01.go:7 +0x28
exit status 2