Channels
Channel 类似 Unix Pipe,是两个 goroutine 之间通信的管道。Channel 带有一个类型,表示通信的数据只能是这种类型。定义一个 channel:
var a chan int
此时 a
的值是 nil
,没有什么意义。一般用 make
来初始化 channel:
a:= make(chan int)
对 channel 有两种操作,都用的是箭头操作符(<-
):
data := <- a // 从 channel a 中读出值并赋值给 data
a <- data // 将 data 写入 channel a
默认情况下,对 channel 的读写会阻塞;比如一个 goroutine 写入了数据,需要等另外一端读取了数据,才能继续运行;反之亦然:
package main
import (
"fmt"
)
func hello(done chan bool) {
fmt.Println("Hello world goroutine")
done <- true
}
func main() {
done := make(chan bool)
go hello(done)
<-done
fmt.Println("main function")
}
运行过程:
main | hello
====================================================================
go hello(done) |
| fmt.Println("Hello world goroutine")
|
(wait for data from done) | done <- true
<-done | (goroutine terminated)
fmt.Println("main function") |
(goroutine terminated) |
死锁
使用 channel 时要注意死锁的情况。一旦向某一 channel 发送了数据,如果没有被读取程序就结束了;或者某一 goroutine 在等待写入 channel 的数据,没有等到程序就结束了,会引起 panic:
package main
func main() {
ch := make(chan int)
ch <- 5
// 换成接收 <- ch,也会造成死锁
}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox249677995/main.go:6 +0x80
单向 channel
上面描述的都是双向 channel,可收可发。你也可以定义单向 channel:
- 双向:
chan int
- 单向:
- 只发:
chan<- int
- 只收:
<-chan int
- 只发:
发跟收区别在于 chan
关键字变成 chan<-
还是 <-chan
。只发是箭头指向 chan
;只收是箭头从 chan
出来。
这样的代码过不了编译,因为从一个只发的 channel 中读取数据:
func receiveData(recvch chan<- int) {
fmt.Println(<-recvch)
}
invalid operation: <-recvch (receive from send-only type chan<- int)
关闭 channel 以及使用 for range 读取 channel
关闭一个 channel:
close(ch)
判断一个 channel 是否已关闭:
v, ok := <-ch
// ok 为 true 时未关闭,为 false 时已关闭
可以用 for 循环消费 channel(但下文描述的 for-range 方式更佳):
for {
v, ok := <-ch
if ok == false {
break
}
fmt.Println("Received ", v, ok)
}
for-range 方式:
for v := range ch {
fmt.Println("Received ",v)
}