心情記錄:最近因爲工作原因開始接觸golang,過年前掃了一遍。以前總是不那麼認真去記一些東西,總想着用的時候在去搜索查詢,但是這樣總是不熟練。
今年想努力向一個優秀的程序員/算法工程師靠攏,所以有空就記錄一下平時看到的技術點吧。
總述:
Effective Go 裏面指出:
If the channel is unbuffered, the sender blocks until the receiver
has received the value. If the channel has a buffer, the sender blocks
only until the value has been copied to the buffer; if the buffer is
full, this means waiting until some receiver has retrieved a value.
無緩衝通道即發送者一直阻塞直到接收者收到value;
緩衝通道即發送者只需阻塞直到value被拷貝到緩衝區,若緩衝區滿了,則等待直到某個接收者收到一個值。
以下例子來自參考1。
1. 無緩衝通道
An unbuffered channel is a channel that needs a receiver as soon as a message is emitted to the 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()
}
代碼步驟解釋(自己翻譯的不準確):
- 創建一個空接收者和發送者列表的通道;
- line16:第一個goroutine發送值“foo”到通道
c
; - 通道
c
需要pool中的sudog
結構體來代表一個sender,這個結構體將代表這個goroutine和值foo; - 現在將sender入隊到
sendq
中; - 該goroutine的狀態變更爲waiting,變更原因爲“chan send”;
- line 23:第二個goroutine將從通道
c
中讀取消息; - 通道使得
sendq
list出列來獲得一個sender,即步驟3中說明的那個結構體; - 該通道將用
memmove
函數拷貝sender發送的值,打包到sudog
結構體中,給我們的變量去讀取通道; - 至此,步驟5中中斷的goroutine現在重新開始,釋放步驟3中的
sudog
。
2. 緩衝通道
以下實例代碼在剛纔的基礎上簡單修改c
的buffer爲2:
package main
import (
"sync"
"time"
)
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 * 1)
println(`Message: `+ <-c)
println(`Message: `+ <-c)
}()
wg.Wait()
}
Goroutines spend 9ms blocked in synchronization waiting for a value from the unbuffered channel while a 50-sized buffer only wait for 1.9ms.
We do now have a confirmation of our previous doubts. The size of the buffer can play an important role in our application performances.