欢迎,来自IP地址为:98.80.143.34 的朋友
初学 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 做到同步或异步的效果,就需要重点关注它们的差异。