什麼是channel
channels 是一種類型安全的消息隊列,充當兩個 goroutine 之間的管道,将通過它同步的進行任意資源的交換。chan 控制 goroutines 互動的能力進而建立了 Go 同步機制。當建立的 chan 沒有容量時,稱為無緩沖通道。反過來,使用容量建立的 chan 稱為緩沖通道。
要了解通過 chan 互動的 goroutine 的同步行為是什麼,我們需要知道通道的類型和狀态。根據我們使用的是無緩沖通道還是緩沖通道,場景會有所不同,是以讓我們單獨讨論每個場景。
無緩沖管道
make :
ch := make(chan struct{})
無緩沖 chan 沒有容量,是以進行任何交換前需要兩個 goroutine 同時準備好。當 goroutine 試圖将一個資源發送到一個無緩沖的通道并且沒有goroutine 等待接收該資源時,該通道将鎖住發送 goroutine 并使其等待。當 goroutine 嘗試從無緩沖通道接收,并且沒有 goroutine 等待發送資源時,該通道将鎖住接收 goroutine 并使其等待。
無緩沖信道的本質是保證同步。
無緩沖channel在消息發送時需要接收者就緒。聲明無緩沖channel的方式是不指定緩沖大小。以下是一個列子:
package main
import (
"sync"
"time"
)
func main() {
c := make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
c <- `foo`
}()
go func() {
defer wg.Done()
time.Sleep(time.Second * 1)
println(`Message: `+ <-c)
}()
wg.Wait()
}
第一個協程會在發送消息
foo
時阻塞,原因是接收者還沒有就緒:這個特性在标準文檔中描述如下:
如果緩沖大小設定為0或者不設定,channel為無緩沖類型,通信成功的前提是發送者和接收者都處于就緒狀态。
effective Go文檔也有相應的描述:
無緩沖channel,發送者會阻塞直到接收者接收了發送的值。
為了更好的了解channel的特性,接下來我們分析channel的内部結構。
package main
import (
"sync"
"time"
"fmt"
)
func main() {
c := make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
c <- "foo"
}()
go func() {
defer wg.Done()
time.Sleep(time.Second)
fmt.Println("message:" + <- c)
}()
wg.Wait()
}
/*
foo
*/
有緩沖管道
buffered channel 具有容量,是以其行為可能有點不同。當 goroutine 試圖将資源發送到緩沖通道,而該通道已滿時,該通道将鎖住 goroutine并使其等待緩沖區可用。如果通道中有空間,發送可以立即進行,goroutine 可以繼續。當goroutine 試圖從緩沖通道接收資料,而緩沖通道為空時,該通道将鎖住 goroutine 并使其等待資源被發送。
package main
import (
"sync"
"time"
"fmt"
)
func main() {
c := make(chan string,2)
var wg sync.WaitGroup
wg.Add(2)
go func(){
defer wg.Done()
c <- "foo"
c <- "bar"
}()
go func(){
defer wg.Done()
time.Sleep(time.Second)
fmt.Println("mesage:" + <- c)
fmt.Println("message:" + <- c)
}()
wg.Wait()
}
/*
mesage:foo
message:bar
*/
追蹤耗時
通過Go工具trace中的
synchronization blocking profile
來檢視測試程式被同步原語阻塞所消耗的時間。接收時的耗時對比:無緩沖channel為9毫秒,緩沖大小為50的channel為1.9毫秒。
- Send 先于 Receive 發生。
- 好處: 延遲更小。
- 代價: 不保證資料到達,越大的 buffer,越小的保障到達。buffer = 1 時,給你延遲一個消息的保障。
參考文章
- https://www.it1352.com/807929.html
- https://www.pengrl.com/p/21027/