Golang channel
本文介紹golang的channel的基礎知識和一些經典的應用正規化.
channel 基礎
channel分類
- buffered/unbuffered channel
按這個標準channel可以分為兩個大類:帶緩衝(buffered)的channel和不帶緩衝(unbuffered)的channel. 定義一個不帶緩的channel
var ch = make(chan bool)
下面這段程式碼會輸出什麼?
package main func main(){ ch :=make(chan int) ch <-10 go task(ch) } func task(ch chan int){ <- ch }
答案是會死鎖。因為unbuffered channel在寫入資料之前需要有接收channle資料的goroutine做好準備。需要對上面程式碼做如下修改:
package main func main(){ ch :=make(chan int) go task(ch) ch <-10 } func task(ch chan int){ <- ch }
帶有緩衝的channel
var ch = make(chan bool, 3)
- unidirectoinal/bidirectional channel
定義一個雙向(bidirectional)channel
var ch = make(chan bool)
雙向的意思是可以在channel上讀或者寫. 定義一個單向只讀channel
var ch = make(<- chan bool)
定義一個單向只寫channel
var ch = make(chan <- bool)
channel的狀態
對處於不同狀態的channel的讀寫操作會有不同的結果
- nil
對nil狀態的channel讀寫都會被阻塞,造成死鎖。
- open
對正常open狀態的channel可以進行正常讀寫。
- closed
往closed狀態的channel寫資料會造成panic,但是讀取不會造成panic,會得到channel型別的零值。
channel的實現原理
實現channel的關鍵資料結構
type hchan struct { qcountuint// total data in the queue dataqsiz uint// size of the circular queue bufunsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closeduint32 elemtype *_type // element type sendxuint// send index recvxuint// receive index recvqwaitq// list of recv waiters sendqwaitq// list of send waiters // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex }
channel裡包含一個環形佇列,用於將goroutine傳送的資料儲存到佇列中。另外recvq
和sendq
佇列用於儲存阻塞在讀和寫操作的goroutine. 當channel中有資料可讀或可寫時,排程器喚醒阻塞在相應佇列中的goroutine.
channle常用的應用正規化
- 生產者消費者模式
- 一對多和多對一的通知
- 互斥鎖
- ratelimiting
- 限制併發數
- 配合select超時控制