go channel

[toc]


Channel

編譯器翻譯

image-20200504174721504

關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

  1. hchan
  2. sudog

hchan

<img src="http://picgo.vipkk.work/20200504175832.png" alt="image-20200504175832482" style="zoom:50%;" />

sudog

<img src="http://picgo.vipkk.work/20200505204907.png" alt="image-20200505204907061" style="zoom:50%;" />

image-20200504175803583

其中buf + sendx + recvx組成ring buffer.

sendq 和 recvq是雙向鏈表,用于儲(chǔ)存等待的g。

sudog中的elem是chan用來傳遞的元素

源碼

init

  1. 判讀是否溢出,溢出則直接panic
  2. 如果要申請的內(nèi)存為0,則把buf 置為racebuf
  3. 如果內(nèi)存不為0,但buf中元素不是指針,則內(nèi)存分配在stack上與chan連續(xù)
  4. 否則分配到堆上
  5. 實(shí)例化 數(shù)據(jù)類型和緩存大小

send

所有的返回都會(huì)unlock

  1. 如果chan為nil,則返回
  2. fasepath,快速失?。═ODO)
  3. 獲取鎖
  4. 如果此時(shí)有receiver在等待,則直接發(fā)給他over,返回發(fā)送成功
    1. 需要將對應(yīng)的receiver 執(zhí)行g(shù)oready
  5. 否則就判斷此時(shí)buf是否已滿,不滿的話放入,更新ringbuffer指針
    1. 需要將對應(yīng)的receiver 執(zhí)行g(shù)oready
  6. 如果buf已滿,將g包裝成sudog,然后park等待
  7. park恢復(fù),釋放內(nèi)存,釋放sudog

recv

  1. 如果chan為nil,則返回
  2. fasepath,快速失敗(TODO)
  3. 獲取鎖
  4. 如果此時(shí)有sender在等待,則直接發(fā)給他over,返回發(fā)送成功
    1. 需要將對應(yīng)的sender 執(zhí)行g(shù)oready
  5. 否則就判斷此時(shí)緩存中是否有數(shù)據(jù),有的話就執(zhí)行內(nèi)存copy,更新ringbuffer指針
    1. 需要將對應(yīng)的sender 執(zhí)行g(shù)oready
  6. 如果緩存為空,則把g包裝為sudog,然后park等待。
  7. park恢復(fù),釋放內(nèi)存,釋放sudog

close

  1. 如果chan為nil,則返回
  2. 加鎖
  3. 判斷是否已經(jīng)關(guān)閉
  4. 把 recvq 和 sendq中的sudog取出到glist
  5. 解鎖
  6. 依次ready glist

掛起和喚醒

當(dāng)發(fā)送行為或者接收行為不能被滿足的時(shí)候,會(huì)掛起。(并非全部,也有非阻塞實(shí)現(xiàn),先跳過)

當(dāng)以上行為得到滿足的時(shí)候,會(huì)被喚醒。

發(fā)送的時(shí)候如果已滿或者沒有緩存的時(shí)候,會(huì)把g包裝成sudog,然后放到chan中,park,等有recv來之后,會(huì)把發(fā)送的sudog取出,執(zhí)行g(shù)oready。(接受同理)

幾種常見行為

行為 表現(xiàn)
發(fā)送時(shí)buf滿了 阻塞
接受時(shí)buf沒數(shù)據(jù) 阻塞
發(fā)送是chan close了 panic
接收時(shí)chan close了 返回默認(rèn)值

Select

編譯器翻譯

<img src="http://picgo.vipkk.work/20200506002833.png" alt="image-20200506002833134" style="zoom:50%;" />

<img src="http://picgo.vipkk.work/20200506002932.png" alt="image-20200506002932143" style="zoom:50%;" />

Code

selectgo是 select中多個(gè)case的實(shí)現(xiàn),具體分析如下

// selectgo implements the select statement.
//
// cas0 points to an array of type [ncases]scase, and order0 points to
// an array of type [2*ncases]uint16. Both reside on the goroutine's
// stack (regardless of any escaping in selectgo).
//
// selectgo returns the index of the chosen scase, which matches the
// ordinal position of its respective select{recv,send,default} call.
// Also, if the chosen scase was a receive operation, it reports whether
// a value was received.
func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
    return 0,false
}

流程

  1. nil處理

  2. shuffle打亂(保證case的隨機(jī)性)

    // generate permuted order
     for i := 1; i < ncases; i++ {
         j := fastrandn(uint32(i + 1))
         pollorder[i] = pollorder[j]
         pollorder[j] = uint16(i)
     }
    
  3. 按照hchan 的address進(jìn)行heap sort 得到lockorder, 這個(gè)順序是對channel加鎖的順序 防止死鎖

  4. 按照lockorder的順序依次對 channel加鎖

  5. 按照pollorder順序依次遍歷ncases

    1. 如果為nil則繼續(xù)
    2. 接收 - > recv
    3. 發(fā)送 -> send
    4. default

    如果都沒有的話則繼續(xù)

  6. 按照lock順序把所有的chan放到對應(yīng)的 等待隊(duì)列

  7. park等待喚醒

  8. 繼續(xù)加鎖

  9. 把未成功執(zhí)行的chan 出隊(duì)列

  10. 解鎖

參考資料

  1. go內(nèi)存模型 https://golang.org/ref/mem

    https://www.pengrl.com/p/34119/ 中文翻譯

問題

  1. releasetime
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容