天天看點

Go Channel執行個體剖析

本文檔主要通過實際例子,GO版本v1.16.6,結合Go channel的資料結構進行分析,hchan裡面的參數是怎麼變化的,同時解析一下hchan裡面buf的讀與寫,queue是怎麼運作的。想要了解Go Channel源碼的可以看,我之前的部落格《GO Channel源碼分析》。

本文大綱如下,會先分析流程,資料結構,最後再執行個體:

  • channel send流程圖
  • channel read流程圖
  • hchan.buf的讀與寫
  • hchan.recvq/sendq的讀與寫
  • channel send執行個體分析
  • channel recv執行個體分析
channel send
Go Channel執行個體剖析
  1. 判斷channel recvq是否有正在等着read的channel
  2. 如果有從recvq中dequeue一個出來sudog(sg)
  3. 如果sg指向的element不為空,則把目前寫入的channel的value給sg,send的資料直接拷貝到目标棧上
  4. 如果sg指向的element已經為空或者執行完第三步,則喚醒之前park住的sg
  5. 如果第一步recvq為空,表示沒有在等待read的channel,則判斷是否目前channel有緩存空間
  6. 有緩存空間則寫入buf中
  7. 沒有緩存空間則初始化一個sudog,寫入sendq,并且gopark挂起
channel read
Go Channel執行個體剖析

和channel send大同小異

  1. 判斷channel sendq是否有正在等着send的channel
  2. 如果有從sendq中dequeue一個出來sudog(sg)
  3. 如果目前channel還有緩存空間,從buf中copy
  4. 如果目前channel沒有緩存空間,則直接把recv的資料copy給sg,從sender的棧上拷貝資料
  5. 如果第一步sendq為空,表示沒有在等待send的channel,判斷否目前channel有緩存空間
  6. 有緩存空間則從buf中讀取
  7. 沒有緩存空間則初始化一個sudog,寫入recvq,并且gopark挂起
hchan.buf讀與寫

channel的buf其實就是一個環形隊列,channel send和recv,分别是對buf的write和read。write的時候就是把寫入value的位址copy到buf中對應的緩存位址去。read就是把buf在源碼中是通過chanbuf來計算出read和write的位址。

在hcan中,datasize,qcount,sendx和recvx就是用來操作buf的參數。datasize是buf的空間大小,qcount是存儲元素的數量,sendx是寫入時的位址下标,recvx是讀取時的位址下标。

Go Channel執行個體剖析
hchan.recvq/sendq的讀與寫

hchan裡面的recvq和sendq就是一個先進先出的queue,enqueue和dequeue可以通過下面的例子看一下。

Go Channel執行個體剖析
channel send執行個體分析

c:=make(chan int,2)

,chan type為int,緩存大小為2

同一個顔色的就是一步

Go Channel執行個體剖析
  1. c < 1:有緩存空間,寫入緩存
  2. c < 1:緩存空間,寫入緩存
  3. c < 1:緩存空間用完,gopark,寫入sendq
channel recv執行個體分析

接着上個例子的結果

Go Channel執行個體剖析
  1. = < c:sendq中有值,dequeue出來,寫入dequeue的channel
  2. = < c:緩存中有值,從緩存中讀
  3. = < c:緩存中還有值,從緩存中讀
  4. = < c:channel沒有緩存了,就寫入recvq,gopark
  5. c < 1:recvq中有值,從recvq dequeue,寫入