php小編柚子在這篇文章中將解答一個常見問題:“為什么Go Channel的緩沖區不能正確限制寫入/讀取?”在Go語言中,Channel是一種用于協程之間通信的機制。當我們使用帶有緩沖區的Channel時,期望能夠通過限制寫入或讀取操作的數量來控制程序的行為。然而,實際上,Channel的緩沖區并不能直接限制寫入/讀取操作的數量。下面將詳細闡述這個問題的原因和解決方案。
問題內容
我正在嘗試使用通道在兩個 go 例程之間進行通信。首先,我創建了整數通道,然后將其作為參數傳遞給 go 例程,該例程打印從 0 到 10 的數字序列。這些程序的輸出沒有意義。
這是主要代碼:
func worker(identifier string, ch chan<- int) { fmt.printf("entering worker %s\n", identifier) for i := 0; i < 10; i++ { fmt.printf("writing %d\n", i) ch <- i } fmt.printf("exiting worker %s\n", identifier) close(ch) } func main() { ch := make(chan int) go worker("1", ch) for v := range ch { fmt.printf("reading %d\n", v) } }
登錄后復制
對于該代碼執行,我得到了以下輸出:
entering worker 1 writing 0 writing 1 reading 0 reading 1 writing 2 writing 3 reading 2 reading 3 writing 4 writing 5 reading 4 reading 5 writing 6 writing 7 reading 6 reading 7 writing 8 writing 9 reading 8 reading 9 exiting worker 1
登錄后復制
請注意,有兩次寫入執行,然后是兩次讀取執行。
后來,我設置了一個緩沖區大小來實現如下功能:
func main() { ch := make(chan int, 3) // <= buffer go worker("1", ch) for v := range ch { fmt.printf("reading %d\n", v) } }
登錄后復制
然后,得到以下輸出:
Entering worker 1 Writing 0 Writing 1 Writing 2 Writing 3 Writing 4 Reading 0 Reading 1 Reading 2 Reading 3 Reading 4 Writing 5 Writing 6 Writing 7 Writing 8 Writing 9 Reading 5 Reading 6 Reading 7 Reading 8 Reading 9 Exiting worker 1
登錄后復制
請注意,現在我們有 5 個寫入執行,然后有 5 個讀取執行。
一旦我們有了代碼和輸出,最后一個問題就來了:為什么這些執行的行為是這樣的?首先,它不是應該每次只讀取和寫入一個數字嗎?除此之外,為什么第二次執行每次讀取和寫入 5 個數字而不是 3 個(因為這是緩沖區大小)?
解決方法
當打印消息時以及何時從通道讀取或寫入數字時,您會混淆。
當寫入發生時,不會發生“寫入”消息。它們發生在寫入之間的某個時刻。同樣,“正在閱讀”消息發生在讀取之間的某個時刻。
這是安排第一個代碼片段的一種方法,它會產生顯示的輸出:
主要嘗試讀取,然后阻塞。
工作人員打印“Writing 0”。
Worker 寫入 0,main 讀取。
工人打印“Writing 1”。
工作線程嘗試寫入 1,但被阻止。
主要打印“Reading 0”。
主要內容為 1。
主要印刷品“閱讀1”。
主要嘗試讀取,然后阻塞。
Control 像這樣在 main 和 Worker 之間不斷傳遞,每個在阻塞之前打印 2 條消息。
同樣,您的第二個片段也可以這樣安排:
主要嘗試讀取,然后阻塞。
Worker 打印“Writing 0”,并將 0 直接發送到 main。
Worker 打印“Writing 1”,并緩沖 1。
Worker 打印“Writing 2”,并緩沖 2。
Worker 打印“Writing 3”,并緩沖 3。
工作線程打印“Writing 4”,并阻止嘗試發送 4。
main 完成被阻塞的讀取,并打印“Reading 0”。
main 讀取緩沖的 1,并打印“Reading 1”。
main 讀取緩沖的 2,并打印“Reading 2”。
main 讀取緩沖的 3,并打印“Reading 3”。
main 讀取 Worker 被阻塞的 4,并打印“Reading 4”。
主要嘗試讀取,然后阻塞。
執行返回到 Worker…