1 从一个简单示例开始
package main
import "fmt"
func main() {
//创建channel
ch := make(chan string)
//往channel发数据
ch <- `data1`
//打印
fmt.Printf(<-ch)
}
执行程序报错
报错协程死锁
原因是只有往发数据方,却没有接收数据方。
在go语言中channel被定义为协程之间的交流通道因此需要两个协程1为发送1为接收
修改代码
package main
import "fmt"
func main() {
//创建channel
ch := make(chan string)
//使用协程执行接收
go func() {
for {
//打印
fmt.Printf(<-ch)
}
}()
//往channel发数据
ch <- `data1`
}
执行成功
结果展示
2 换一种channel创建方式
package main
import "fmt"
func main() {
//创建channel
ch := make(chan string , 10)
//发送数据
for i:= 0 ; i< 10 ; i++ {
ch <- fmt.Sprintf("data%d" , i)
}
//接收打印数据
for {
fmt.Println(<-ch)
}
}
依然执行程序报错
结果展示
还是协程死锁,但是数据都打印出来
两个知识点
- 创建channel的方式有两种
1 make(chan type)
2 make(chan type , bufferSize)
第一种创建了common channel
第二种创建了buffer channel
两种chan的区别在于buffer channel可以缓存部分数据也就是说在没有达到buffer channel最大size之前,哪怕没有接收方也可以正常执行。
所以数据被正常的发送和打印了 - 不是说可以正常执行吗为啥还报错
仔细观察可以发现报错大体一样还是有区别的
一个是 chan send 一个是 chan receive
也就是发送和接收有一个卡住都会死锁
修改代码
package main
import (
"fmt"
)
func main() {
//创建channel
ch := make(chan string , 10)
//发送数据
for i:= 0 ; i< 10 ; i++ {
ch <- fmt.Sprintf("data%d" , i)
}
//接收打印数据
for i:= 0 ; i < 10 ; i++ {
fmt.Println(<-ch)
}
}
执行成功
结果展示
3 由2引发的问题
通过第二段的代码会产生一个问题,怎么肯定发送者一定发送了buffer size的数据哪?
代码示例
package main
import (
"fmt"
)
func main() {
//创建channel
ch := make(chan string , 10)
//发送数据
for i:= 0 ; i< 5 ; i++ {
ch <- fmt.Sprintf("data%d" , i)
}
//接收数据
for i:= 0 ; i < 10 ; i++ {
fmt.Println(<-ch)
}
}
执行报错
结果展示
报错内容:chan receive 死锁
原因是我只往chan发了五个数据
到第六次循环执行接收chan的时候自然就死锁了
那么在不确定chan长度的时候怎么接收哪?
来段代码
package main
import (
"fmt"
)
func main() {
//创建channel
ch := make(chan string , 10)
//发送数据
for i:= 0 ; i< 5 ; i++ {
ch <- fmt.Sprintf("data%d" , i)
}
//获得channel长度
l := len(ch)
//接收数据
for i:= 0 ; i < l ; i++ {
fmt.Println(<-ch)
}
}
可以先获得channel的长度
package main
import (
"fmt"
"time"
)
func main() {
//创建channel
ch := make(chan string , 10)
//发送数据
for i:= 0 ; i< 5 ; i++ {
ch <- fmt.Sprintf("data%d" , i)
}
timer := time.NewTicker(time.Microsecond)
//接收数据
for {
select {
case msg := <- ch:
fmt.Println(msg)
case <-timer.C:
}
}
}
也可以使用select语法
都可以执行成功
结果展示
实际工作中第二种方式会经常用到
这种方式限定于只是暂时没数据的情况(暂时接收不到数据没关系过一会儿在试试)
还有一种情况数据全部发送完了不会再有数据了,这时怎么做那?
上代码
package main
import (
"fmt"
)
func main() {
//创建channel
ch := make(chan string , 10)
//发送数据
for i:= 0 ; i< 5 ; i++ {
ch <- fmt.Sprintf("data%d" , i)
}
//声明chan关闭
close(ch)
//接收数据
for {
n,ok := <- ch
if !ok {
break
}
fmt.Println(n)
}
}
当发送方确定不会有数据后发送后,声明chan关闭了
接收时通过第二参数判断接收数据
另一种写法
package main
import (
"fmt"
)
func main() {
//创建channel
ch := make(chan string , 10)
//发送数据
for i:= 0 ; i< 5 ; i++ {
ch <- fmt.Sprintf("data%d" , i)
}
//声明chan关闭
close(ch)
//第二种接收数据写法
for data := range ch {
fmt.Println(data)
}
}
结果是一样的
以上介绍了golang channel 的三个主要知识点
1 简单channel使用
2 缓存channel使用
3 关闭channel方法