Go 语言那些事儿之管道的关闭
之前我们提到了怎么定义管道,讲了管道的读取和写入。那么今天我们就来讲一讲管道的关闭。
先来看一个简单的例子:
 func main() {  ch := make(chan int,10)  for i := 0;i < 5;i++{    ch <- i*i  }  close(ch)
  for x:= range ch {    fmt.Println(x)  }}
   复制代码
 
先思考一下它会输出什么?
没错它的输出结果是:
这段的代码就是定义一个管道,然后遍历零到四,将零到四的平方塞入管道。我们知道这个管道定义时,被赋予了 10 的缓存能力,所以能够缓存 10 个单位的数据。而我们这里只需要存 5 个单位,你会想是不是还有 5 个单位是空闲的?是的。
下面我们再看一看多个协程并发读写管道:
 func main()  {  ch := make(chan int,10)
  go func() {    for i:=0;i<5;i++{      ch <- i * i      fmt.Println("写入",i*i)      time.Sleep(1 * time.Second)
    }        // The close built-in function closes a channel, which must be either    // bidirectional or send-only. It should be executed only by the sender,        // never the receiver, and has the effect of shutting down the channel after        // the last sent value is received. After the last value has been received        // from a closed channel c, any receive from c will succeed without        // blocking, returning the zero value for the channel element. The form        //  x, ok := <-c        // will also set ok to false for a closed channel.    close(ch)    fmt.Println("管道已关闭,写入协程结束")  }()  go func() {    for x:= range ch{      fmt.Println("读出",x)    }    fmt.Println("读取协程结束")  }()
  time.Sleep(6 * time.Second)  fmt.Println("GAME OVER")}
   复制代码
 
运行结果是:
 写入 0读出 0读出 1写入 1写入 4读出 4写入 9读出 9写入 16读出 16读取协程结束管道已关闭,写入协程结束GAME OVER
   复制代码
 
这段代码的作用是建立一个缓存能力为 10 的管道,建立两条协程。一条协程用来将零到四的平方写入管道,写完之后关闭管道。另一条协程是用来将管道内的数据读出。
由于两条协程是并发的,所以一条协程往管道里写入一条数据,马上另一条协程就往管道里读取一条数据。这里那条写入数据的协程关闭了管道,那么就会通知那边不需要再继续从管道中读取数据了,那么另外一条协程就不会往管道里面读取数据了。如果没有close(ch),也就是不关闭管道,那么另一条协程就会不断读取管道,直到主协程正常结束,那条子协程才会结束。
我们需要注意,内建函数 close 函数是用来关闭通道的,并且只有双向的和仅发送的管道才能被关闭。
还有一点需要注意的是,这个 close 函数需要由发送方执行,而不是由接收方执行。从一个关闭的通道接收完最后一个值后,从该管道接收的任何值都将成功,不会阻塞,并返回零值。但是对于x, ok := <-c,将会为关闭的管道的 ok 值赋予false值。
评论