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. 一个经典错误```gopackage mainimport "fmt"func main(){var msg chan intmsg = make(chan int)msg<- 1data := <- msgfmt.Println(data)}
5. 通过启动协程消费channel
package mainimport ("fmt""sync")func main(){var msg chan intmsg = make(chan int)go func(){data := <- msgfmt.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 <- 1c <- 2close(c)c <- 3}
但是从这个关闭的channel中不但可以读取出已发送的数据,还可以不断的读取零值:
c := make(chan int, 10)c <- 1c <- 2close(c)fmt.Println(<-c) //1fmt.Println(<-c) //2fmt.Println(<-c) //0fmt.Println(<-c) //0
但是如果通过range读取,channel关闭后for循环会跳出:
c := make(chan int, 10)c <- 1c <- 2close(c)for i := range c {fmt.Println(i)}
通过i, ok := <-c可以查看Channel的状态,判断值是零值还是正常读取的值。
c := make(chan int, 10)close(c)i, ok := <-cfmt.Printf("%d, %t", i, ok) //0, false
channel可以用在goroutine之间的同步。
下面的例子中main goroutine通过done channel等待worker完成任务。 worker做完任务后只需往channel发送一个数据就可以通知main goroutine任务完成。
