go channel
channel
channel一般用于goroutine之间的通信;初始化chan :=make(chan int)
可以比较:同一引用的chan比较返回true,可以用于map的key
3种操作:
- 发送
chan <- x,对一个关闭的通道发送数据会导致panic - 接受
x <- chan,对关闭的通道的接收操作会立即返回获取一个通道类型的零值 - 关闭
- 重复关闭 channel 会导致 panic。
- 向关闭的 channel 发送数据会 panic。
- 从关闭的 channel 关读数据不会 panic,读出 channel 中已有的数据之后再读就channel 类似的默认值,比如 chan int 类型的 channel 闭之后读取到的值为 0。
对于上述第三点,有个点需要注意:从channel中读出的是是默认零值还是channel关闭了返回的值.可以用类似map中取值的方式判断
ch := make(chan int, 10)
...
close(ch)
// ok-idiom
val, ok := <-ch
if ok == false {
// channel closed
}
类型
通道不是必须要关闭的,gc回收取决于是否可以访问
- 无缓冲通道
接收操作会阻塞,直到另一头执行发送,当通道关闭接收操作理解返回。同时所有类型通道在关闭后发送操作会引发panic,接收操作会立即返回一个零值(用在取消goroutine上)
可以使用以下方式来判断通道是否关闭
data ,ok := <- chan, ok
通道也可用于range
- 单向通道
func a(<- chan int),该通道只能接收数据
func a( chan<- int),该通道只能发送数据
非法操作会在编译时报错
其实这两种通道被声明时都是chan类型的,只不过在函数中为了限定行为而加入的限制.
- 缓冲通道
make(chan int ,3)
上述通道可以看做容量为1的缓冲通道,用作队列,关闭后允许接收完通道中的数据
案例 & 用法
一般select只会走一次,break基本上没什么作用,所以一般要配合for循环使用, break 跳出外循环
out: for {
select {
case e, ok := <-c1:
...
break out
case e, ok := <-c2:
...
}
for range
func producer(ch chan int) {
for _, v := range values {
ch <- v
}
}
超时
select {
case <- ch:
// get data from ch
case <- time.After(2 * time.Second)
// read data from ch timeout
}
goroutine泄露
func a () {
pipe := make(chan int)
go func(){ <-pipe}
pipe <- 1
pipe <- 2
pipe <- 3
//fatal error: all goroutines are asleep - deadlock!
}
函数执行完后会有两个 goroutine卡住,无法被回收,叫goroutine泄露。办法是改成缓冲通道
而且修改下代码
func a () {
pipe := make(chan int,3)
go func(){ <-pipe}
pipe <- 1
pipe <- 2
pipe <- 3
//fatal error: all goroutines are asleep - deadlock!
}
channel都需要对应的接收者和发送者,如果已经明确的缺少了一端(事实上已经不可能在发送或接收数据了)就会爆出上述错误;而第一段代码会安全退出是因为main本来不会等待其他协程,因为通道操作而阻塞,等到接收到第一个数字时就会退出不会等待其他协程发送.
取消goroutine
通过chan的广播方式,确切的说是关闭通道接收操作会立即返回的特点.在各个goroutine中接收一个chan,当需要停止时,关闭这个chan,这些goroutine就会收到取消信号.
channel 读写
当channel的接收两端一端在接收而通道中没有值,且没有另一端再发送数据(没有可能发送数据的代码了),会出错
IO
IO包
Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
读取len(p)字节写入p。
当读入数据不足len(p)时,会返回能够读取的数据,而不会继续等待
当遇到错误或者到达末尾时,会返回读取的字节数以及一个错误,当读到末尾时,err返回一个EOF或者nil。但不管如何,发生这两种情况下次调用读入长度肯定为(0,EOF)。所以要求每次都要读入的字节。
ReaderAt
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
从底层输入流读取off偏移量位置len(p)的数量的字节。
方法会等待读满返回,当n<len(p)时,会返回err中说明为啥没读满。
方法不会改变底层流的状态。
Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
写入操作。
返回写入的字节数以及导致写入提前结束的错误。
WriteAt
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}
可以并行写入的方法
也是返回写入长度以及导致提前结束的错误。
操作不会影响底层流的偏移量。