Go语言channel在应用中相当的常见了,其中channel分两类,一种是 buffered channel,另一种是 unbuffered channel。
Unbuffered Channel
没缓冲的channel,我们看个例子
1 2 3 4 5 6 7 8 9 10 11 12 13
| package main
import ( "fmt" "time" )
func main() { go func() { fmt.Println("Goroutine.") }() time.Sleep(1 * time.Second) }
|
如果我们不休眠一秒是看不到输出的。这里我们设置的Timeout是1秒,我们默认了这个Goroutine不会执行耗时任务,假如执行时间未知,此方法就行不通。可以引入channel解决这个问题,请看示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import ( "fmt" "time" )
func main() { end := make(chan bool) go func() { time.Sleep(time.Second * 2) fmt.Println("Goroutine.") end <- true }() <-end }
|
end
是一个Unbuffered channel,无论Goroutine执行多久结束,主线程都会等待其完全执行完成,从这里看出来
Unbuffered channel是可以用来做同步的,相反的Buffered channel可以做异步。上面的例子可以做一些变化,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import ( "fmt" "time" )
func main() { end := make(chan bool) go func() { time.Sleep(time.Second * 2) fmt.Println("Goroutine.") <-end }() end <- true }
|
这两种结果是一样的。只是方式上存在差异,前者读阻塞,后者写阻塞。
Buffered Channel
上面的例子改成带缓冲的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import ( "fmt" "time" )
func main() { end := make(chan bool, 1) go func() { time.Sleep(time.Second * 2) fmt.Println("Goroutine.") <-end }() end <- true }
|
程序不会有任何输出,此时的end
是带缓冲的,没等Goroutine执行,end
就写入true
主程就退出了。但是我们换一种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import ( "fmt" "time" )
func main() { end := make(chan bool, 1) go func() { time.Sleep(time.Second * 2) fmt.Println("Goroutine.") end <- true }() <-end }
|
此时是可以输出的。虽然我们设置缓冲,但是<-end
是读阻塞的,因为此时end
并没有值。所以Buffered Channel也是可以做同步的,只是应用在多个Goroutine。但是不建议这样做,如果不小心对channel操作就会造成意想不到的一些BUG,此时应该考虑使用context包进行上下文管理。