在Go語言中,有一種特殊的通道類型叫做Buffered Channel(緩衝通道),它在通道中儲存一定數量的元素。當通道中的元素數量達到設定的上限時,寫入操作會被阻塞住,直到有其他協程從通道中讀取元素。相反,當通道中的元素數量為零時,讀取操作也會被阻塞住,直到有其他協程向通道中寫入元素。這種阻塞機制可以有效控制協程之間的同步和通訊。在本文中,我們將詳細介紹Go語言中Buffered Channel的阻塞機制。
在《Tour of Go》中,範例程式碼是這樣給出的:
package main import "fmt" func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) fmt.Println(<-ch) }
它執行良好並印出來
1 2
此行為與此練習的描述不同,其中指出:
<code> Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty </code>
在ch <- 2
行之後,ch
is 已滿,並且由於我們只運行1 個單獨的Goroutine,即主Goroutine,因此該Goroutine 應該被阻塞,直到ch
is 被接收者消耗,因此程式碼不應該到達fmt.Println(<-ch)
行,但應該說類似
<code> fatal error: all goroutines are asleep - deadlock! </code>
但是,由於情況並非如此,我很困惑,並尋求指導。
這是我寫的另一段程式碼
chh := make(chan int, 2) go func() { chh <- 1 fmt.Printf("chh after 1: %v, %v\n", cap(chh), len(chh)) chh <- 2 fmt.Printf("chh after 2: %v, %v\n", cap(chh), len(chh)) chh <- 3 fmt.Printf("chh after 3: %v, %v\n", cap(chh), len(chh)) }() fmt.Println(<-chh) fmt.Println(<-chh) fmt.Println(<-chh)
執行結果為
1 chh after 1: 2, 0 chh after 2: 2, 0 chh after 3: 2, 1 2 3
這更令人困惑。這次有另一個 goroutine 進行發送。我的期望是,在第一個 fmt.Println(<-chh)
期間,主 goroutine 應該被阻塞。調度程序將選擇運行匿名函數的 goroutine,並且它應該執行到 chh <- 2
,然後它會阻塞自身,調度程序再次恢復到主 goroutine。然而,如結果所示,第二個 goroutine 在 chh <- 1
之後立即被阻塞。為什麼會這樣呢?
編輯: 我仍然不明白為什麼我的本地首先打印 1 。當我在遠端伺服器上嘗試使用 go Playground 時,它顯示出不同的行為,現在與我的期望一致。
已知channel是由3個佇列組成(接收goroutines、發送goroutines ans value buffer),當匿名函式執行時,channel chh
的狀態為(sending:empty,valuebuffer: empty,receiving:[main] )
。
正在運行的子 Goroutine 只是將值直接推入主 Goroutine,而沒有實際將其傳遞到值緩衝區。這就是為什麼chh
推送後1
的長度是0
。
該通道可容納兩人。兩次發送可以成功而不會阻塞。 第三個不能。只有當通道在發送之前已滿時,發送才會阻塞。
以上是Go 的 Buffered Channel 的阻塞機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!