一. Channel简介
Channel
是Golang的2大核心之一,类似Linux的管道,为并发Goroutine提供一种同步通信机制,借助于Channel不同的Goroutine之间可以相互通信。
- 创建channel:
make(chan type)
type表示具体数据类型,除了支持常规的int、float64、string等类型外,还支持struct、interface等ch1 := make(chan int) ch2 := make(chan float64) ch3 := make(chan string) ch4 := make(chan struct{}) ch5 := make(chan interface{})
- 发送数据:
ch <- v
,ch <-
ch在左边表示发送数据到channel ch中 - 接收数据:
x := <- ch
,<- ch
ch在右边表示从channel ch中接收数据
根据发送和接收数据,可以分成以下3类Channel
- 可以发送&可以接收: make(chan type),默认创建channel都是可以发送和接收数据
- 只可以发送: make(chan<- type),只可以发送数据channel
- 只可以接收: make(<-chan type),只可以接收数据channel
和slice类似,channel在make的时候也可以设置队列容量
,根据队列容量大小,可以分成以下2类Channel
- 无缓冲channel: make没有设置队列容量或者队列容量为0,表示channel是一个无缓存channel,例如make(chan type)
- 缓冲channel: make设置队列容量不为0,表示channel是一个缓存channel,例如make(chan type, size)
channel可以被关闭,可以使用内置的close
函数关闭一个channel。
channel具有以下几个特点
从一个nil channel接收数据会一直阻塞
var c1 chan int go func() { for { v := <- c1 fmt.Println(v) //一直阻塞直到程序退出 } }() time.Sleep(5 * time.Second) fmt.Println("exit ~")
如果channel没有数据,只要channel未关闭,从channel中接收数据会一直阻塞直到有数据为止
// 无缓冲channel c1 := make(chan int) go func() { <- c1 //阻塞直到c1有数据 fmt.Println("receiver from c1") }() fmt.Println(time.Now().Unix()) time.Sleep(2* time.Second) fmt.Println(time.Now().Unix()) c1 <- 1 time.Sleep(5* time.Second) fmt.Println("exit ~") // 有缓冲channel c2 := make(chan int, 100) go func() { <- c2 //阻塞直到c2有数据 fmt.Println("receiver from c2") }() fmt.Println(time.Now().Unix()) time.Sleep(2* time.Second) fmt.Println(time.Now().Unix()) c2 <- 1 time.Sleep(5* time.Second) fmt.Println("exit ~")
无缓冲channel,发送数据和接收数据同时发生。如果没有receiver接收数据(<- chan),则sender发送数据会一直阻塞;如果没有sender发送数据(chan <-),则receiver接收数据会一直阻塞
// 没有receiver接收数据 c1 := make(chan int) go func() { c1 <- 1 //阻塞直到有receiver fmt.Println("sender 1 to c1") }() time.Sleep(20 * time.Second) fmt.Println("exit ~") // 没有sender发送数据 c2 := make(chan int) go func() { <- c2 //阻塞直到有sender fmt.Println("receiver from c2") }() time.Sleep(20 * time.Second) fmt.Println("exit ~")
有缓冲channel,当队列容器未满的时候sender不会阻塞,当队列容量满的时候sender会阻塞,当队列容量为空的时候receiver会阻塞
c1 := make(chan int, 10) go func() { for i := 0; i < 100; i++ { c1 <- i fmt.Printf("send %d to c1\n", i) //i为10的时候开始阻塞,直到receiver开始消费 } }() time.Sleep(5 * time.Second) fmt.Println("sleep 5 seconds done ~") go func() { for { <- c1 //消费完所有数据后,receiver开始阻塞 } }() time.Sleep(20 * time.Second) fmt.Println("exit ~")
关闭nil channel会panic (panic: close of nil channel)
var c1 chan int close(c1) //panic: close of nil channel fmt.Println("exit ~")
重复关闭channel会panic (panic: close of closed channel)
c1 := make(chan int) close(c1) time.Sleep(5 * time.Second) close(c1) //panic: close of closed channel fmt.Println("exit ~")
向已经关闭channel发送数据会panic (panic: send on closed channel)
c1 := make(chan int) close(c1) time.Sleep(5 * time.Second) c1 <- 1 //panic: send on closed channel fmt.Println("exit ~")
从已经关闭的channel读取数据不会阻塞,如果channel为空读到对应类型默认值,例如int默认值是0,指针默认值为nil
c1 := make(chan int, 5) go func() { for i := 1; i <= 5; i++ { c1 <- i } }() time.Sleep(5 * time.Second) close(c1) go func() { for { v := <- c1 fmt.Println(v) //打印出 1 2 3 4 5 0 0 0 0 ... } }() time.