[Go] Golang Channel
Contents
Don’t communicate by sharing memory, share memory by communicating.
介绍
channel是GO语言级别提供的goroutine间的通信方式,channel是类型相关的,需要在声明channel时指定类型,
所以channel只能传递一种类型的值。channel使用make内置方法声明,分为无缓冲(unbuffered)和缓冲(buffered)的通道。
语法上的区别在于声明时的长度,默认是可读可写的,关键字chan后面跟上相应的类型表示仅可读写此类型的值,加上箭头表示只读或只写。
使用箭头 <-
来读写一个channel,无缓冲的channel常用于同步场景,当向一个无缓冲的channel发送消息,另一端无接收,
或者从一个无缓冲的channel接收消息,另一端无发送时,当前goroutine会阻塞。
|
|
使用注意
- 向一个nil channel(只声明未赋值)发送/读取数据,会一直阻塞;
- 关闭一个nil channel,会导致panic;
- 只能关闭一个双向或者可写的channel;
- 向一个已经关闭的channel发送数据,会导致panic;
- 对于同一个channel,多次调用close(),会导致panic;
- 对一个已关闭的channel写数据,会导致panic,但可以继续读取数据;
- 当channel关闭并且缓冲区为空时,继续读取数据不会panic,会读到channel对应类型的0值;
|
|
使用
无缓冲channel
|
|
有缓冲channel
|
|
有缓冲的channel只有当缓冲区满或者空,写入和读取的时候才会发生阻塞,所以缓冲的channel可以当做简单的队列来使用
如果在channel为空时再读取一次,则会发生死锁
|
|
运行错误信息: fatal error: all goroutines are asleep - deadlock! 死锁的原因在于channel的另一端没有goroutine写入了,当缓冲的channel为空时,主goroutine会一直阻塞在读取,同时也没有其他goroutine可以调度。
读取超时处理
|
|
写入超时处理
|
|
遍历
for range
|
|
for range 遍历方式是阻塞型遍历方式; 在遍历时,如果channel 没有关闭,那么会一直等待下去,出现 deadlock 的错误;如果在遍历时channel已经关闭,那么在遍历完数据后自动退出遍历。
for select
|
|
select可以处理非阻塞式消息发送、接收及多路选择; select关键字的灵感来源与unix中的I/O多路复用函数select; select中有case代码块,用于channel发送或接收消息,任意一个case代码块准备好时,执行其对应内容; 多个case代码块准备好时,随机选择一个case代码块并执行;所有case代码块都没有准备好,则等待; 还可以有一个default代码块,所有case代码块都没有准备好时执行default代码块;
channel closing principle
- 不要从接收端关闭channel,
- 不要在多个并发发送端关闭channel,
- 个receivers,一个sender
- sender通过关闭data channel 让sender不再发送数据
- 不能让任意receivers来关闭data channel
- 一个receivers,n个sender
- receiver通过关闭一个额外的signal channel通知sender停止发送数据
- 不能让receiver关闭data channel,也不能让任意sender来关闭
- m个receivers,n个sender
- 通过通知一个moderator(仲裁者)关闭额外的signal channel来停止发送和接收
- 不能让任意receivers和senders关闭data channel
- 也不能让任意receivers和senders通过关闭一个额外的signal channel,可能导致signal channel多次关闭panic