【Golang詳解】go語言中的channel


Channel

底層數據結構
type hchan struct {
	qcount   uint           // 當前隊列中剩余元素個數
	dataqsiz uint           // 環形隊列長度,即可以存放的元素個數
	buf      unsafe.Pointer // 環形隊列指針
	elemsize uint16         // 每個元素的大小
	closed   uint32	        // 標識關閉狀態
	elemtype *_type         // 元素類型
	sendx    uint           // 隊列下標,指示元素寫入時存放到隊列中的位置
	recvx    uint           // 隊列下標,指示元素從隊列的該位置讀出
	recvq    waitq          // 等待讀消息的goroutine隊列
	sendq    waitq          // 等待寫消息的goroutine隊列
	lock mutex              // 互斥鎖,chan不允許並發讀寫
}

waitqsudog 的一個雙向鏈表

1. type waitq struct {
2.    first *sudog
3.    last  *sudog
4. }

sudog 實際上是對 goroutine 的一個封裝,表示一個在等待隊列中的goroutine,該結構

存儲了兩個分別指向前后sudog的指針用來構成鏈表

發送數據
  • 如果當前channel的recvq上存在已經被阻塞的Goroutine(也就是說有goroutine在等待讀消息),那么會直接將數據發送給當前的Goroutine並將其設置成下一個運行的Goroutine(設置處理器runnext屬性,不會立刻調度)
  • 如果channel存在緩沖區並且還有空余位置,會直接將數據存儲到緩存區sendx所在的位置上
  • 如果不滿足上述兩種情況,會創建一個sudog結構並將其加入channel的sendq隊列中,當前Goroutine陷入阻塞等待其他協程從Channel接收數據
接收數據
  • 如果Channel為空,那么會直接讓出處理器的使用權。

  • 如果Channel已經關閉並且緩存區沒有任何數據,會直接返回

  • 如果Channel的sendq隊列中存在掛起的Goroutine(說明有阻塞發送的goroutine),根據緩沖區的大小分別處理不同的情況:

    如果 Channel 不存在緩沖區, 將 Channel 發送隊列中 Goroutine 存儲的數據拷貝到目標內存地址中;

    如果 Channel 存在緩沖區,將隊列中的數據拷貝到接收方的內存地址;將發送隊列頭的數據拷貝到緩沖區中,釋放一個阻塞的發送方;

  • 如果Channel的緩沖區存在數據(沒有阻塞的發送Goroutine),會將緩沖區中的數據拷貝到接收方的內存地址、清除隊列中的數據並完成收尾工作。

  • 當 Channel 的發送隊列中不存在等待的 Goroutine 並且緩沖區中也不存在任何數據時,會使用 runtime.sudog 將當前 Goroutine 包裝成一個處於等待狀態的 Goroutine 將其加入到接收隊列中並陷入休眠等待調度器的喚醒;

關閉通道

當 Channel 是一個空指針或者已經被關閉時,Go 語言運行時都會直接崩潰並拋出異常

處理完了這些異常的情況之后就可以開始執行關閉 Channel 的邏輯了,close 函數先上一把大鎖,接着把所有掛在這個 channel 上的 sender 和 receiver 全都連成一個 sudog 鏈表,再解鎖。最后,再將所有的 sudog 全都喚醒。

喚醒之后,sender 會檢測到channel已經關閉,panic。從一個有緩沖的 channel 里讀數據,當 channel 被關閉,依然能讀出有效值。只有當返回的 ok 為 false 時,讀出的數據才是無效的,為對應類型的零值。

x, ok := <-ch
產生panic的情況

向一個關閉的 channel 進行寫操作;關閉一個 nil 的 channel;重復關閉一個 channel。

總結

channel緩存區是由循環隊列實現的

channel的等待隊列是一個雙向鏈表

channel 的發送和接收操作本質上都是 “值的拷貝”

從一個有緩沖的 channel 里讀數據,當 channel 被關閉,依然能讀出有效值。發數據會panic。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM