在源码中channel
的数据结构大体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| type hchan struct { qcount uint dataqsiz uint buf unsafe.Pointer elemsize uint16 closed uint32 elemtype *_type sendx uint recvx uint recvq waitq sendq waitq
lock mutex }
|
创建一个容量为 6 的,元素为 int 型的 channel 数据结构如下 :

必须值得注意的是:
关闭 channel 后,对于等待接收者而言,会收到一个相应类型的零值
优雅关闭channel
, 根据 sender 和 receiver 的个数,分下面几种情况:
- 一个 sender, M 个 receiver, 直接在sender端关闭,用双值语句检查是否关闭
- N 个 sender,一个 reciver,提供一个关闭信号channel,reciver端发送信号(关闭信号channel)
- N 个 sender, M 个 receiver,协调好全体发送者,只关闭一次即可。
操作 |
nil channel |
closed channel |
not nil, not closed channel |
close |
panic |
panic |
正常关闭 |
读 <- ch |
阻塞 |
读到对应类型的零值 |
阻塞或正常读取数据。缓冲型 channel 为空或非缓冲型 channel 没有等待发送者时会阻塞 |
写 ch <- |
阻塞 |
panic |
阻塞或正常写入数据。非缓冲型 channel 没有等待接收者或缓冲型 channel buf 满时会被阻塞 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| func main() { taskCh := make(chan int, 100) go worker(taskCh)
for i := 0; i < 10; i++ { taskCh <- i }
select { case <-time.After(time.Hour): } }
func worker(taskCh <-chan int) { const N = 5 for i := 0; i < N; i++ { go func(id int) { for { task := <- taskCh fmt.Printf("finish task: %d by worker %d\n", task, id) time.Sleep(time.Second) } }(i) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| var limit = make(chan int, 3)
func main() { for _, w := range work { go func() { limit <- 1 w() <-limit }() } }
|
go-questions: channel