channel 提供了一种通信机制,通过它,一个 goroutine 可以想另一 goroutine 发送消息。channel 本身还需关联了一个类型,也就是 channel 可以发送数据的类型。例如: 发送 int 类型消息的 channel 写作 chan int 。
说明
- channel是引用类型,必须初始化
- 初始化是通过make初始化的
- 总结:make初始化三种类型:slice, map和channel
1. 申明
var msg chan int
2. 初始化和关闭
msg = make(chan int) #不带缓冲区
msg = make(chan int, 16) #带缓冲区
3. 放数据和取数据
```go package main
import “fmt”
func main(){ var msg chan int msg = make(chan int, 1) msg<- 1 data := <- msg fmt.Println(data) }
v, ok := <-ch #它可以用来检查Channel是否已经被关闭了。
<a name="fAGsh"></a>
## 4. 一个经典错误
```go
package main
import "fmt"
func main(){
var msg chan int
msg = make(chan int)
msg<- 1
data := <- msg
fmt.Println(data)
}
5. 通过启动协程消费channel
package main
import (
"fmt"
"sync"
)
func main(){
var msg chan int
msg = make(chan int)
go func(){
data := <- msg
fmt.Println(data)
}()
msg <- 1 #试试把这一行写到11行之后看看
}
6. 通过for range获取channel的数据
range c
产生的迭代值为Channel中发送的值,它会一直迭代直到channel被关闭。上面的例子中如果把close(c)
注释掉,程序会一直阻塞在for …… range
那一行。
func main() {
go func() {
time.Sleep(1 * time.Hour)
}()
c := make(chan int)
go func() {
for i := 0; i < 10; i = i + 1 {
c <- i
}
close(c)
}()
for i := range c {
fmt.Println(i)
}
fmt.Println("Finished")
}
7. 关闭
内建的close方法可以用来关闭channel。
总结一下channel关闭后sender的receiver操作。
如果channel c已经被关闭,继续往它发送数据会导致panic: send on closed channel
:
import "time"
func main() {
go func() {
time.Sleep(time.Hour)
}()
c := make(chan int, 10)
c <- 1
c <- 2
close(c)
c <- 3
}
但是从这个关闭的channel中不但可以读取出已发送的数据,还可以不断的读取零值:
c := make(chan int, 10)
c <- 1
c <- 2
close(c)
fmt.Println(<-c) //1
fmt.Println(<-c) //2
fmt.Println(<-c) //0
fmt.Println(<-c) //0
但是如果通过range
读取,channel关闭后for循环会跳出:
c := make(chan int, 10)
c <- 1
c <- 2
close(c)
for i := range c {
fmt.Println(i)
}
通过i, ok := <-c
可以查看Channel的状态,判断值是零值还是正常读取的值。
c := make(chan int, 10)
close(c)
i, ok := <-c
fmt.Printf("%d, %t", i, ok) //0, false
channel可以用在goroutine之间的同步。
下面的例子中main goroutine通过done channel等待worker完成任务。 worker做完任务后只需往channel发送一个数据就可以通知main goroutine任务完成。