Go语言Channel的Buffered与Unbuffered

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包进行上下文管理。