IP Address:18.215.159.156


初学  Go 语言时,最大的挑战就是了解 Channel 的使用时机及差异。而 Channel 又分为两种,一种是 buffered channel,另一种是 unbuffered channel,本文将用几个简单的例子带大家了解这两种 channel 的差异,让初学者可以很快的了解 channel 使用方法。

Unbuffered Channel

在 Go 语言内使用 goroutine 是很常见的,但是我们该如何透过 Channel 来解决同步问题,请看下面的例子:

package main

import (
	"fmt"
	"time"
)

func main() {
	go func() {
		fmt.Println("GO GO GO")
	}()
	time.Sleep(1 * time.Second)
}

由于 main 函数执行完成后,程序就会结束,所以后面设定了等待一秒来让 goroutine可以正常执行完成。但是一般开发模式不会加上 Timeout,而是使用 Unbuffered Channel 方式来达到一样的效果。

package main

import (
   "fmt"
)

func main() {
   c := make(chan bool)
   go func() {
      fmt.Println("GO GO GO")
      c <- true
   }()
   <-c
}

示例首先用  make(chan bool) 来建立一个 channel,在 main 函数最后用 <-c 代表需要等待读出一个 channel 值,main 函数才会结束,这时候就达到了跟用 Sleep 一样的效果,接着将程序代码改成如下:

package main

import (
   "fmt"
)

func main() {
   c := make(chan bool)
   go func() {
      fmt.Println("GO GO GO")
      <-c
   }()
   c <- true
}

执行代码后会发现得到同样的结果,这是为什么呢?因为 unbufferd channel 的用途就是:当在程序代码内丢了一个值到 channel,这时候 main 函数就需要等到一个 channel 值被读出来才会结束。所以不管是在 goroutine 内读或写,main 都需要等到一个写一个读完成后才会结束。

这就是用 unbuffered channel 来达到同步的效果,也就是保证读写都需要执行完毕才可以结束主程序。

buffered Channel

那 buffered Channel 又会怎么样呢?差异就在于声明方式不同,请看下面的例子:

package main

import (
   "fmt"
)

func main() {
   c := make(chan bool, 1)
   go func() {
      fmt.Println("GO GO GO")
      <-c
   }()
   c <- true
}

声明 buffered channel 是通过 make(chan bool, 1) 后面有带容量值,可以通过容量值来设定些 channel 可以容纳几个值。

如果执行上面的程序代码,会发现完全没有输出任何内容。原因是什么,buffered channel 就是只要有容量,你都可以塞值进去,但是不用读出来没关系。所以当丢了 c <- true 进去后,主程序不会等到读出来才结束,造成 goroutine没有执行打印主程序就已经结束,所有没有内容显示出来。这也就是buffered channel同unbuffered channel 最大的区别,可以把它理解为一个异步模型。

结论

需要特别强调的是 unbuffered channel 就是代表在主程序内,需要等到读或写都完成,main 才可以完整结束(读跟写 buffered channel 需要在不同的 goroutine 才不会被 block);而 buffered channel 相反,你可以一直丢资料进去 Channel 内,但是不需要读出来(前提是 buffered channel 空间够大不会溢出),所以 main 才提前结束。如果您想要用 channel 做到同步或异步的效果,就需要重点关注它们的差异。

发表评论

电子邮件地址不会被公开。 必填项已用*标注