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) }