channel
goroutine之间数据通信的桥梁
一、创建chan
1.无缓存的chan
// 1.使用内置的make
ch1 := make(chan int)
var ch2 chan int
// 2.声明只读通道
ch2 := make(<-chan string)
var ch3 <- chan int
// 3.声明只写通道
ch3 := make(chan<- string)
var ch2 chan <- int
注意:如果没有指定是写通道还是读通道,默认的就是双向的,可读可写
把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。
2.有缓存的chan
ch1 := make(chan int, capacity) capacity:容量
当capacity=0时,channel是无缓冲阻塞读写的,
当capacity>0时,channel有缓冲,是非阻塞的,直到写满capacity个元素才阻塞写入
ch2 := make(chan int) 相当于ch2 := make(chan int, 0) ,capacity为0
// 声明带10个缓冲的通道
ch3 := make(chan string, 10)
注意:
- 如果定义了缓存的大小,写入的大于了就会报错
- 通过 len 函数可以获得 chan 中的元素个数,通过 cap 函数可以得到 channel 的缓存长度
fatal error: all goroutines are asleep - deadlock!
- 当缓存满时,发送消息将阻塞
- 当 channel 为空时,读取操作会造成阻塞
3.有缓存和无缓存的区别
- 无缓存的chan,chan为空,读取将阻塞,chan不为空,写入将阻塞 (只能有一个)
- 有缓存的chan,当缓存满了写入将阻塞,当chan为空读取将阻塞
二、关闭通道
ch := make(chan int)
defer close(ch)
注意:
- 关闭一个未初始化的 channel 会产生 panic
var ch chan int
defer close(ch)
panic: close of nil channel
此时的chan是nil,未初始化
ch := make(chan int)就没问题,会分配内存
- 重复关闭同一个 channel 会产生 panic
ch := make(chan int)
defer close(ch)
defer close(ch)
panic: close of closed channel
已经关闭的了chan,不能再次关闭
- 已关闭的chan中不能写入数据,
ch := make(chan int)
defer close(ch)
ch <- 3
fatal error: all goroutines are asleep - deadlock!
- 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息
- 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for x := range ch {
fmt.Println(x)
}
x, ok := <- ch
fmt.Println(x, ok)
注意:这里不能用defer close,会报错
fatal error: all goroutines are asleep - deadlock!
defer 是在程序最后执行的,还没有关闭,就绪从goroutine中读取,肯定是不行的
二、goroutine使用chan通信
channel 一定要初始化后才能进行读写操作,否则会永久阻塞
1.写入
ch := make(chan int, 10)
ch <- 3
ch <- 4
ch <- 5
缓存已满,继续写入会报错
2.读取
ch := make(chan int, 10)
ch <- 3
ch <- 4
ch <- 5
// 一个一个读取,直到读取完
fmt.Println(<- ch)
fmt.Println(<- ch)
fmt.Println(<- ch)
// 再继续读取回报错:fatal error: all goroutines are asleep - deadlock!
3.goroutine通信
猫主子和铲屎官的日常:
package main
import (
"fmt"
"sync"
)
// 猫主子和铲屎官日常
var Wg = sync.WaitGroup{}
func Cat(ch chan string){
foot,ok := <- ch
fmt.Println(ok)
if ok {
fmt.Printf("猫主子吃了%s\n",foot)
}else {
fmt.Printf("铲屎官该投食了")
}
Wg.Done()
}
func Man(ch chan string,foot string) {
ch <- foot
fmt.Printf("铲屎官投食了%s\n",foot)
Wg.Done()
}
func main() {
foot := "罐头"
ch := make(chan string,10)
defer close(ch)
go Man(ch,foot)
for i:=0; i<10;i++ {
Wg.Add(2)
go Cat(ch)
go Man(ch,foot)
}
Wg.Wait()
}