本文介紹golang的channel的基礎(chǔ)知識和一些經(jīng)典的應(yīng)用范式.
channel 基礎(chǔ)
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在寫入數(shù)據(jù)之前需要有接收channle數(shù)據(jù)的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的狀態(tài)
對處于不同狀態(tài)的channel的讀寫操作會有不同的結(jié)果
- nil
對nil狀態(tài)的channel讀寫都會被阻塞,造成死鎖禀综。
- open
對正常open狀態(tài)的channel可以進行正常讀寫。
- closed
往closed狀態(tài)的channel寫數(shù)據(jù)會造成panic菇存,但是讀取不會造成panic夸研,會得到channel類型的零值依鸥。
channel的實現(xiàn)原理
實現(xiàn)channel的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // 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里包含一個環(huán)形隊列,用于將goroutine發(fā)送的數(shù)據(jù)保存到隊列中贱迟。另外 recvq
和 sendq
隊列用于保存阻塞在讀和寫操作的goroutine. 當(dāng)channel中有數(shù)據(jù)可讀或可寫時,調(diào)度器喚醒阻塞在相應(yīng)隊列中的goroutine.
channle常用的應(yīng)用范式
- 生產(chǎn)者消費者模式
- 一對多和多對一的通知
- 互斥鎖
- ratelimiting
- 限制并發(fā)數(shù)
- 配合select超時控制