Golang channel 之 數(shù)據(jù)結(jié)構(gòu)

下一篇:Golang channel 之 寫操作 send

channel的作用

channel被設(shè)計(jì)用來(lái)實(shí)現(xiàn)goroutine間的通信,按照golang的設(shè)計(jì)思想:以通信的方式共享內(nèi)存。

channel的內(nèi)存布局

例如如下代碼中的make函數(shù)會(huì)在堆上分配一個(gè)runtime.hchan類型的數(shù)據(jù)結(jié)構(gòu),ch是存在于函數(shù)f棧幀上的一個(gè)指針,指向堆上的hchan數(shù)據(jù)結(jié)構(gòu)。

func f() {
    ch := make(chan int)
    ...
}

至于為什么是堆上的一個(gè)結(jié)構(gòu)體:首先,要實(shí)現(xiàn)channel這樣的復(fù)雜功能,肯定不是幾個(gè)字節(jié)可以搞定的,所以需要一個(gè)struct來(lái)實(shí)現(xiàn);其次,這種被設(shè)計(jì)用來(lái)實(shí)現(xiàn)協(xié)程間通信的組件,其作用域和生命周期不可能僅限于某個(gè)函數(shù)內(nèi)部,所以golang直接將其分配在堆上。

channel的數(shù)據(jù)結(jié)構(gòu)

下面結(jié)合在channel中的作用,解讀一下hchan中都有哪些字段:
1)協(xié)程間通信肯定涉及到并發(fā)訪問,所以要有鎖來(lái)保護(hù)整個(gè)數(shù)據(jù)結(jié)構(gòu):

type hchan struct {
    ...
    lock mutex
}

2)channel分為“無(wú)緩沖”和“有緩沖”兩種,對(duì)于有緩沖channel來(lái)講,需要有相應(yīng)的內(nèi)存來(lái)存儲(chǔ)數(shù)據(jù),實(shí)際上就是一個(gè)數(shù)組,需要知道數(shù)組的地址、容量、元素的大小,以及數(shù)組的長(zhǎng)度也就是已有元素個(gè)數(shù),加上這幾個(gè)字段后,上面的結(jié)構(gòu)體就變成了這樣:

type hchan struct {
    qcount   uint           // 數(shù)組長(zhǎng)度,即已有元素個(gè)數(shù)
    dataqsiz uint           // 數(shù)組容量,即可容納元素個(gè)數(shù)
    buf      unsafe.Pointer // 數(shù)組地址
    elemsize uint16         // 元素大小
    ...
}

3)因?yàn)間olang運(yùn)行時(shí)中內(nèi)存復(fù)制、垃圾回收等機(jī)制依賴數(shù)據(jù)的類型信息,所以hchan中還要有一個(gè)指針,指向元素類型的類型元數(shù)據(jù):

type hchan struct {
    ...
    elemtype *_type // 元素類型
    ...
}

4)channel支持交替的讀寫(稱send為寫,recv為讀,更簡(jiǎn)潔),有緩沖channel內(nèi)的緩沖數(shù)組會(huì)被作為一個(gè)“環(huán)型”來(lái)使用,當(dāng)下標(biāo)超過數(shù)組容量后會(huì)回到第一個(gè)位置,所以需要有兩個(gè)字段記錄當(dāng)前讀和寫的下標(biāo)位置:

type hchan struct {
    ...
    sendx    uint   // 下一次寫下標(biāo)位置
    recvx    uint   // 下一次讀下標(biāo)位置
    ...
}

5)當(dāng)讀和寫操作不能立即完成時(shí),需要能夠讓當(dāng)前協(xié)程在channel上等待,當(dāng)條件滿足時(shí),要能夠立即喚醒等待的協(xié)程,所以要有兩個(gè)等待隊(duì)列,分別針對(duì)讀和寫:

type hchan struct {
    ...
    recvq    waitq  // 讀等待隊(duì)列
    sendq    waitq  // 寫等待隊(duì)列
    ...
}

6)channel是能夠被close的,所以要有一個(gè)字段記錄是否已經(jīng)close掉了:

type hchan struct {
    ...
    closed   uint32
    ...
}

最后整合起來(lái),runtime.hchan結(jié)構(gòu)是這個(gè)樣子:

type hchan struct {
    qcount   uint           // 數(shù)組長(zhǎng)度,即已有元素個(gè)數(shù)
    dataqsiz uint           // 數(shù)組容量,即可容納元素個(gè)數(shù)
    buf      unsafe.Pointer // 數(shù)組地址
    elemsize uint16         // 元素大小
    closed   uint32
    elemtype *_type // 元素類型
    sendx    uint   // 下一次寫下標(biāo)位置
    recvx    uint   // 下一次讀下標(biāo)位置
    recvq    waitq  // 讀等待隊(duì)列
    sendq    waitq  // 寫等待隊(duì)列
    lock     mutex
}

本篇先到這里,至于channel的讀寫操作和select機(jī)制,留到后面的文章中解讀。

下一篇:Golang channel 之 寫操作 send

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

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

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