面試企業(yè) 好未來、米哈游、跟誰學(xué),字節(jié)跳動(dòng)、美團(tuán)、網(wǎng)易、新浪、滴滴、小米
題目解析 GOLANG ROADMA社區(qū)
答案(知北游)+
背景:
- Go語言提供了一種不同的并發(fā)模型–通信順序進(jìn)程(communicating sequential processes,CSP)。
- 設(shè)計(jì)模式:通過通信的方式共享內(nèi)存
- channel收發(fā)操作遵循先進(jìn)先出(FIFO)的設(shè)計(jì)
type hchan struct { qcount uint // channel中的元素個(gè)數(shù) dataqsiz uint // channel中循環(huán)隊(duì)列的長度 buf unsafe.Pointer // channel緩沖區(qū)數(shù)據(jù)指針 elemsize uint16 // buffer中每個(gè)元素的大小 closed uint32 // channel是否已經(jīng)關(guān)閉,0未關(guān)閉 elemtype *_type // channel中的元素的類型 sendx uint // channel發(fā)送操作處理到的位置 recvx uint // channel接收操作處理到的位置 recvq waitq // 等待接收的sudog(sudog為封裝了goroutine和數(shù)據(jù)的結(jié)構(gòu))隊(duì)列由于緩沖區(qū)空間不足而阻塞的Goroutine列表 sendq waitq // 等待發(fā)送的sudogo隊(duì)列,由于緩沖區(qū)空間不足而阻塞的Goroutine列表 lock mutex // 一個(gè)輕量級(jí)鎖}
channel創(chuàng)建:
ch := make(chan int, 3)
- 創(chuàng)建channel實(shí)際上就是在內(nèi)存中實(shí)例化了一個(gè)hchan結(jié)構(gòu)體,并返回一個(gè)chan指針
- channle在函數(shù)間傳遞都是使用的這個(gè)指針,這就是為什么函數(shù)傳遞中無需使用channel的指針,而是直接用channel就行了,因?yàn)閏hannel本身就是一個(gè)指針
channel發(fā)送數(shù)據(jù):
ch <- 1ch <- 2
- 檢查 recvq 是否為空,如果不為空,則從 recvq 頭部取一個(gè) goroutine,將數(shù)據(jù)發(fā)送過去,并喚醒對(duì)應(yīng)的 goroutine 即可。
- 如果 recvq 為空,則將數(shù)據(jù)放入到 buffer 中。
- 如果 buffer 已滿,則將要發(fā)送的數(shù)據(jù)和當(dāng)前 goroutine 打包成 sudog 對(duì)象放入到 sendq中。并將當(dāng)前 goroutine 置為 waiting 狀態(tài)。
channel接收數(shù)據(jù):
<-ch<-ch
- 檢查sendq是否為空,如果不為空,且沒有緩沖區(qū),則從sendq頭部取一個(gè)goroutine,將數(shù)據(jù)讀取出來,并喚醒對(duì)應(yīng)的goroutine,結(jié)束讀取過程。
- 如果sendq不為空,且有緩沖區(qū),則說明緩沖區(qū)已滿,則從緩沖區(qū)中首部讀出數(shù)據(jù),把sendq頭部的goroutine數(shù)據(jù)寫入緩沖區(qū)尾部,并將goroutine喚醒,結(jié)束讀取過程。
- 如果sendq為空,緩沖區(qū)有數(shù)據(jù),則直接從緩沖區(qū)讀取數(shù)據(jù),結(jié)束讀取過程。
- 如果sendq為空,且緩沖區(qū)沒數(shù)據(jù),則只能將當(dāng)前的goroutine加入到recvq,并進(jìn)入waiting狀態(tài),等待被寫goroutine喚醒。
channel規(guī)則:
操作 | 空channel | 已關(guān)閉channel | 活躍中的channel |
close(ch) | panic | panic | 成功關(guān)閉 |
ch<- v | 永遠(yuǎn)阻塞 | panic | 成功發(fā)送或阻塞 |
v,ok = <-ch | 永遠(yuǎn)阻塞 | 不阻塞 | 成功接收或阻塞 |