channelinactive触发后不关闭channel_go那些事儿|channel使用及其实现原理
目錄?
channel背景
channel基本用法
channel應用場景
channel實現(xiàn)原理
channel數(shù)據(jù)結構
channel實現(xiàn)方式
channel注意事項
閑聊
歡迎加入我的公眾號【邁莫coding】 一起pk大廠
channel是Go的核心類型,是Go語言內(nèi)置的類型,你無需引包,就能使用它。你可以把它看作一個管道,在Go語言中流傳著一句話,"執(zhí)行業(yè)務處理的goroutine不要通過共享內(nèi)存通信,要通過channel管道進行共享數(shù)據(jù)"。
channel和Go的另一種特性goroutine一起為并發(fā)編程提供了優(yōu)雅的,便利的方案,來應對并發(fā)場景。
2channel基本用法channel的基本用法非常簡單,它提供了三種類型,分別為只能接收,只能發(fā)送,既能接收也能發(fā)送這三種類型。因此它的語法為:
chanstruct{} chan chan string // 既能接收也能發(fā)送我們把既能發(fā)送也能接收的chan被稱為雙向chan,把只能接收或者只能發(fā)送的chan稱為單向chan。其中,"這個箭頭總是射向左邊的,元素類型總在最右邊。如果箭頭指向 chan,就表示可以往 chan 中塞數(shù)據(jù);如果箭頭遠離 chan,就表示 chan 會往外吐數(shù)據(jù)。
通過make關鍵字,我們可以初始化一個chan,未初始化的chan的零值為nil。你可以設置他的容量,第二個參數(shù)為緩沖池的容量大小,也可以理解為即使chan未消費完,也可以存儲數(shù)據(jù)。
make(chan int, 8)?如果chan中還有數(shù)據(jù),那么從這個chan中接收數(shù)據(jù)就不會阻塞,如果chan中數(shù)據(jù)未達到隊列容量,那么向該chan中存儲數(shù)據(jù)也不會阻塞,反之會阻塞。
還有一個知識點要記住:nil 是 chan 的零值,是一種特殊的 chan,對值是 nil 的 chan 的發(fā)送接收調(diào)用者總是會阻塞。
接下來,我們用代碼來學習一下chan的三種類型
只能接收數(shù)據(jù)的chan
代碼示例
package main import "fmt"// a 表示只能接收數(shù)據(jù)的chanfunc goChanA(a chan int) { b := fmt.Println("只能接收數(shù)據(jù)的channal[a]接收到的數(shù)據(jù)值為", b)}func main() { ch := make(chan int, 2) go goChanA(ch) // 往ch中寫入數(shù)據(jù)值 ch 2 time.Sleep(time.Second)}結果
只能接收數(shù)據(jù)的channal[a]接收到的數(shù)據(jù)值為 2?只能發(fā)送數(shù)據(jù)的chan
代碼示例
package main import "fmtfunc main() { ch := make(chan ch }往 chan 中發(fā)送一個數(shù)據(jù)使用“ch
這里的 ch 是 chan int 類型或者是 chan
3channel應用場景
數(shù)據(jù)交流:當作并發(fā)的 buffer 或者 queue,解決生產(chǎn)者 - 消費者問題。多個 goroutine 可以并發(fā)當作生產(chǎn)者(Producer)和消費者(Consumer)。
數(shù)據(jù)傳遞:一個goroutine將數(shù)據(jù)交給另一個goroutine,相當于把數(shù)據(jù)的擁有權托付出去。
信號通知:一個goroutine可以將信號(closing,closed,data ready等)傳遞給另一個或者另一組goroutine。
任務編排:可以讓一組goroutine按照一定的順序并發(fā)或者串行的執(zhí)行,這就是編排功能。
鎖機制:利用channel實現(xiàn)互斥機制。
channel數(shù)據(jù)結構
channel一個類型管道,通過它可以在goroutine之間發(fā)送消息和接收消息。它是golang在語言層面提供的goroutine間的通信方式。
眾所周知,Go依賴于稱為CSP(Communicating Sequential Processes)的并發(fā)模型,通過 Channel實現(xiàn)這種同步模式。?
channel結構體
//path:src/runtime/chan.gotype hchan struct { qcount uint // 當前隊列列中剩余元素個數(shù) dataqsiz uint // 環(huán)形隊列長度,即可以存放的元素個數(shù) buf unsafe.Pointer // 環(huán)形隊列列指針 elemsize uint16 // 每個元素的?? closed uint32 // 標識關閉狀態(tài) elemtype *_type // 元素類型 sendx uint // 隊列下標,指示元素寫?入時存放到隊列列中的位置 x recvx uint // 隊列下標,指示元素從隊列列的該位置讀出 recvq waitq // 等待讀消息的goroutine隊列 sendq waitq // 等待寫消息的goroutine隊列 lock mutex // 互斥鎖,chan不允許并發(fā)讀寫}從數(shù)據(jù)結構可以看出channel由隊列、類型信息、goroutine等待隊列組成。? ? ? ?
channel實現(xiàn)方式
chan內(nèi)部實現(xiàn)了一個緩沖隊列作為緩沖區(qū),隊列的長度是創(chuàng)建chan時指定的。
下圖展示了可緩存6個元素的channel示意圖:
dataqsiz:指向隊列的長度為6,即可緩存6個元素
buf:指向隊列的內(nèi)存,隊列中還剩余兩個元素
qcount:當前隊列中剩余的元素個數(shù)
sendx:指后續(xù)寫入元素的位置
recvx:指從該位置讀取數(shù)據(jù)
等待隊列
從channel中讀數(shù)據(jù),如果channel緩沖區(qū)為空或者沒有緩沖區(qū),當前goroutine會被阻塞;向channel中寫數(shù)據(jù),如果channel緩沖區(qū)已滿或者沒有緩沖區(qū),當前goroutine會被阻塞。
被阻塞的goroutine將會被掛在channel的等待隊列中:
因讀阻塞的goroutine會被向channel寫入數(shù)據(jù)的goroutine喚醒
因?qū)懽枞膅oroutine會被從channel讀數(shù)據(jù)的goroutine喚醒
下面展示了一個沒有緩沖區(qū)的channel,有幾個goroutine阻塞等待數(shù)據(jù):
注意,一般情況下recvq和sendq至少有一個為空。只有一個例外,那就是同一個goroutine使用select語句向channel一邊寫數(shù)據(jù)一邊讀數(shù)據(jù)。
向channel寫數(shù)據(jù)
流程圖:
詳細過程
如果recvq隊列不為空,說明緩沖區(qū)沒有數(shù)據(jù)或者沒有緩沖區(qū),此時直接從recvq等待隊列中取出一個G,并把數(shù)據(jù)寫入,最后把該G喚醒,結束發(fā)送過程;
如果緩沖區(qū)有空余位置,則把數(shù)據(jù)寫入緩沖區(qū)中,結束發(fā)送過程;
如果緩沖區(qū)沒有空余位置,則把數(shù)據(jù)寫入G,將當前G寫入sendq隊列,進入休眠,等待被讀goroutine喚醒;
從channel讀數(shù)據(jù)
流程圖
詳細過程
如果等待發(fā)送隊列sendq不為空,且沒有緩沖區(qū),直接從sendq隊列中取出G,把G中數(shù)據(jù)讀出,最后把G喚醒,結束讀取過程;
如果等待發(fā)送隊列sendq不為空,說明緩沖區(qū)已滿,從緩沖隊列中首部讀取數(shù)據(jù),從sendq等待發(fā)送隊列中取出G,把G中的數(shù)據(jù)寫入緩沖區(qū)尾部,結束讀取過程;
如果緩沖區(qū)中有數(shù)據(jù),則從緩沖區(qū)取出數(shù)據(jù),結束讀取過程;
向已經(jīng)關閉的channel中寫入數(shù)據(jù)會發(fā)生Panic
關閉已經(jīng)關閉的channel會發(fā)生Panic
關閉值為nil的channel會發(fā)生Panic
6閑聊
讀完文章,自己是不是和channel管道的cp率又提高了
我是邁莫,歡迎大家和我交流
原創(chuàng)不易,覺得文章寫得不錯的小伙伴,點個贊? 鼓勵一下吧~
7歡迎加入我的公眾號【邁莫coding】 一起pk大廠
邁莫coding歡迎客官的到來
總結
以上是生活随笔為你收集整理的channelinactive触发后不关闭channel_go那些事儿|channel使用及其实现原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分析:windows下cmd默认的编码是
- 下一篇: 安装SQLyog教程