[toc]
Channel
編譯器翻譯

關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
- hchan
- 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%;" />

其中buf + sendx + recvx組成ring buffer.
sendq 和 recvq是雙向鏈表,用于儲(chǔ)存等待的g。
sudog中的elem是chan用來傳遞的元素
源碼
init
- 判讀是否溢出,溢出則直接panic
- 如果要申請的內(nèi)存為0,則把buf 置為racebuf
- 如果內(nèi)存不為0,但buf中元素不是指針,則內(nèi)存分配在stack上與chan連續(xù)
- 否則分配到堆上
- 實(shí)例化 數(shù)據(jù)類型和緩存大小
send
所有的返回都會(huì)unlock
- 如果chan為nil,則返回
- fasepath,快速失?。═ODO)
- 獲取鎖
- 如果此時(shí)有receiver在等待,則直接發(fā)給他over,返回發(fā)送成功
- 需要將對應(yīng)的receiver 執(zhí)行g(shù)oready
- 否則就判斷此時(shí)buf是否已滿,不滿的話放入,更新ringbuffer指針
- 需要將對應(yīng)的receiver 執(zhí)行g(shù)oready
- 如果buf已滿,將g包裝成sudog,然后park等待
- park恢復(fù),釋放內(nèi)存,釋放sudog
recv
- 如果chan為nil,則返回
- fasepath,快速失敗(TODO)
- 獲取鎖
- 如果此時(shí)有sender在等待,則直接發(fā)給他over,返回發(fā)送成功
- 需要將對應(yīng)的sender 執(zhí)行g(shù)oready
- 否則就判斷此時(shí)緩存中是否有數(shù)據(jù),有的話就執(zhí)行內(nèi)存copy,更新ringbuffer指針
- 需要將對應(yīng)的sender 執(zhí)行g(shù)oready
- 如果緩存為空,則把g包裝為sudog,然后park等待。
- park恢復(fù),釋放內(nèi)存,釋放sudog
close
- 如果chan為nil,則返回
- 加鎖
- 判斷是否已經(jīng)關(guān)閉
- 把 recvq 和 sendq中的sudog取出到glist
- 解鎖
- 依次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
}
流程
nil處理
-
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) } 按照hchan 的address進(jìn)行heap sort 得到lockorder, 這個(gè)順序是對channel加鎖的順序 防止死鎖
按照lockorder的順序依次對 channel加鎖
-
按照pollorder順序依次遍歷ncases
- 如果為nil則繼續(xù)
- 接收 - > recv
- 發(fā)送 -> send
- default
如果都沒有的話則繼續(xù)
按照lock順序把所有的chan放到對應(yīng)的 等待隊(duì)列
park等待喚醒
繼續(xù)加鎖
把未成功執(zhí)行的chan 出隊(duì)列
解鎖
參考資料
-
go內(nèi)存模型 https://golang.org/ref/mem
問題
- releasetime