go channel

2021-04-10  本文已影响0人  n_xy

channel

channel一般用于goroutine之间的通信;初始化chan :=make(chan int)
可以比较:同一引用的chan比较返回true,可以用于map的key

3种操作:
  1. 发送
    chan <- x,对一个关闭的通道发送数据会导致panic
  2. 接受
    x <- chan,对关闭的通道的接收操作会立即返回获取一个通道类型的零值
  3. 关闭

对于上述第三点,有个点需要注意:从channel中读出的是是默认零值还是channel关闭了返回的值.可以用类似map中取值的方式判断

ch := make(chan int, 10)
...
close(ch)

// ok-idiom 
val, ok := <-ch
if ok == false {
    // channel closed
}

类型

通道不是必须要关闭的,gc回收取决于是否可以访问

  1. 无缓冲通道
    接收操作会阻塞,直到另一头执行发送,当通道关闭接收操作理解返回。同时所有类型通道在关闭后发送操作会引发panic,接收操作会立即返回一个零值(用在取消goroutine上)
    可以使用以下方式来判断通道是否关闭
 data ,ok := <- chan, ok

通道也可用于range

  1. 单向通道
    func a(<- chan int) ,该通道只能接收数据
    func a( chan<- int) ,该通道只能发送数据
    非法操作会在编译时报错

其实这两种通道被声明时都是chan类型的,只不过在函数中为了限定行为而加入的限制.

  1. 缓冲通道
    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) 
}

可以并行写入的方法
也是返回写入长度以及导致提前结束的错误。
操作不会影响底层流的偏移量。

上一篇 下一篇

猜你喜欢

热点阅读