channel用於主進程、協程之間的通訊。異步
1.同步模式
channel默認爲同步模式,即不建立緩衝區,發送和接收須要一一配對,否則發送方會被一直阻塞,直到數據被接收。須要注意的是,同步的channel不能在一個協程中發送&接收,由於會被阻塞而永遠跑不到接收的語句。一個最簡單的例子:ui
package main import "fmt" func main() { data := make(chan int) go func() { for d := range data {//經過range不斷地處理data fmt.Println(d) } }() data <- 1//發送要放在接收協程跑起來後面,由於發送後會阻塞等待接收 data <- 2 data <- 3 close(data) }
或者用下面這種方法接收channel,若是!ok說明data被close:code
go func() { for { if d, ok := <-data; ok { fmt.Println(d) } } }()
2. 異步模式
異步模式channel有緩衝區,若是緩衝區已滿,發送的主進程或者協程會被阻塞,若是未滿不會被阻塞,若是爲空,接收的協程會被阻塞。基於這種性質每每須要有個同步channel去控制主進程是否退出,不然有可能協程還未處理完全部的信息,主進程已經退出。另外須要注意的是,異步的channel用完要close,否則處理這個的channel會被阻塞,造成死鎖。協程
package main import "fmt" func main() { data := make(chan int, 3) canQuit := make(chan bool) //阻塞主進程,防止未處理完就退出 go func() { for d := range data {//若是data的緩衝區爲空,這個協程會一直阻塞,除非被channel被close fmt.Println(d) } canQuit <- true }() data <- 1 data <- 2 data <- 3 data <- 4 data <- 5 close(data) //用完須要關閉,不然goroutine會被死鎖 <-canQuit //解除阻塞 }
3.select的使用
其實,實際項目中一般這樣用channel。進程
package main import "fmt" import "time" import "os" const ( MAX_REQUEST_NUM = 10 CMD_USER_POS = 1 ) var ( save chan bool quit chan bool req chan *Request ) type Request struct { CmdID int16 Data interface{} } type UserPos struct { X int16 Y int16 } func main() { newReq := Request{ CmdID: CMD_USER_POS, Data: UserPos{ X: 10, Y: 20, }, } go handler() req <- &newReq time.Sleep(2000 * time.Millisecond) save <- true close(req) <-quit } func handler() { for { select { case <-save: saveGame() case r, ok := <-req: if ok { onReq(r) } else { fmt.Println("req chan closed.") os.Exit(0) } } } } func init() { req = make(chan *Request, MAX_REQUEST_NUM) save = make(chan bool) quit = make(chan bool) } func saveGame() { fmt.Printf("Do Something With Save Game.\n") quit <- true } func onReq(r *Request) { pos := r.Data.(UserPos) fmt.Println(r.CmdID, pos) }