channel分爲有buffer的和沒有buffer的。
沒有buffer的能夠當成有buffer可是buffersize爲0的狀況。
buffer數據結構:數組
type hchan struct { qcount uint // 當前chan中有多少數據 dataqsiz uint // 環形數組隊列的大小,也就是咱們定義的緩衝區大小 buf unsafe.Pointer // 指向環形數組隊列的指針 elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // 發送時插入的位置(環形數組的下標) recvx uint // 接收時取數據的位置(環形數組的下標) recvq waitq // 接收鏈表,當buf爲空的時候,打包goroutine現場後放在這裏 sendq waitq // 發送鏈表,當buf滿的時候,打包goroutine現場後放在這裏 // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex }
像圖中發送數據到channel中,每次qcount和sendx會隨之變化,sendx會在插入前標誌當前的插入位置變到插入後標誌下一個數據插入位置(因爲是環形數組,因此若是在最後位置插入後索引歸0)
當buf裏面的數據滿的時候,再往裏面發送數據,此時qcount==dataqsize表示滿,此時咱們會將當前G的現場與channel打包成一個sudog的結構,鏈在sendq上。數據結構
咱們知道,sendq中存放的是等待發送的sudog,那麼一樣的recvq存放的就是等待接收的sudog。能夠想象到,當recvq中有sudog節點的時候就說明咱們的緩衝區已經沒有數據能夠取了,纔會將接收的g放到recvq中。此時,咱們須要發送的內容應該當即被拿走,不應再放到buf或sendq中。
完整的發送流程以下:ui
一樣的,當作從channel中接收數據的動做時,會先判斷buf是否爲空,爲空的話進行現場打包成sudog鏈在recvq的鏈表上。
完整的接收流程以下:
與發送流程有所不一樣的是,當buf數據滿而且sendq中有sudog的時候,咱們還須要判斷是否有緩衝區。this
若是有緩衝區的話咱們須要維護buf:spa
咱們能夠從接收流程中發現,咱們會在buf爲空的時候纔會往recvq追加sudog,那麼也就是說在接收流程中,recvq只要不爲空那就說明buf是空的,那麼沒有緩衝區和有緩衝區也都是等價於空的buf,因此無需判斷。
可是在接收流程中,若是sendq不爲空的話。指針
掛起與喚醒:code