入坑 go 也快一年了,從今天開始會定期分享一下 Go 語言學習過程中的一些基礎知識。
go 語言中的管道, 主要是用於協程之間的通訊, 比 UNIX 的管道更加輕量和易用。
我們先看一下管道的資料結構:
type hchan struct {
gcount uint // 環形佇列剩餘元素個數
dataqsiz uint // 環形佇列長度
buf unsafe.Pointer // 環形佇列指標
elemsize uint16 // 每個元素大小
closed uint32 // 標識關閉狀態
elemtype *_type // 元素型別
sendx uint // 下一個元素寫入時的下標
recvx uint // 下一個元素讀取時的下標
recvq waitq // 等待讀訊息的佇列
sendq waitq // 等待寫訊息的佇列
lock mutex // 互斥鎖, 保障管道無法並行讀寫
}
原始碼連結:
https://github.com/golang/go/blob/0d0193409492b96881be6407ad50123e3557fdfb/src/runtime/chan.go#L33
通過上述資料結構, 我們可以理解管道是由三部分組成的:
環形佇列
讀寫等待佇列
佇列元素基本資訊
從管道讀取資料時, 如果管道緩衝區為空或者沒有緩衝區, 那麼當前協程就會阻塞, 然後放入 recvq 佇列中。
往管道寫入資料時, 如果管道緩衝區為空或者緩衝區滿了, 那麼當前協程就會阻塞, 然後放入 sendq 佇列中。
讀阻塞的協程會被新來的寫資料的協程喚醒。
寫阻塞的協程會被新來的讀資料的協程喚醒。
同時上述資料結構中, 我們可以看到一個管道中只能傳遞一種元素型別。 如果想資料型別動態化, 可以傳遞 interface。
管道的操作:
初始化有兩種方式:
變數宣告:
var ch chan int // 宣告一個新的管道
使用 make:
ch1 := make(chan string) // 無緩衝管道
ch1 := make(chan string 3) // 有緩衝管道
管道的讀寫是通過操作符: 「<-」控制的,管道在左邊表示把右側資料寫入到管道中, 管道在右邊表示讀取管道資料賦值給左側變數。
ch1 := make(chan string) // 初始化
ch1 <- "gjl"; // 把 gjl 字串寫入到管道中
c := <- ch1; // 讀取管道資料並交給 c 變數
fmt.Println(c) // 輸出
同時也可以通過操作符來限制管道的讀寫許可權。
舉個栗子