为什么需要channel?
前面使用全局变量加锁同步来解决goroutine的通讯,但不完美。
(1)主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置10秒,仅仅是估算。
(2)如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随主线程的退出而销毁。
(3)通过全局变量加锁同步来实现通讯,也并不利于多个协程对全局变量的读写操作。
(4)上面种种分析都在呼唤一个新的通讯机制——channel。
一、channel的介绍
1、channel本质上就是一种数据结构——队列。
2、数据是先进先出。
3、线程安全,多goroutine访问时,不需要加锁,就是说channel本身就是线程安全的。
4、channel是有类型的,一个string的channel只能存放string类型数据。
![](https://img.haomeiwen.com/i6201627/69f186c5d456d2ea.png)
二、管道的基本使用
1、定义/声明channel
![](https://img.haomeiwen.com/i6201627/2d18b4c98fcc2d50.png)
2、初始化
![](https://img.haomeiwen.com/i6201627/5154efabceaf7d0b.png)
3、向channel中写入(存放)数据
![](https://img.haomeiwen.com/i6201627/3a5a1df9ee51e8fc.png)
4、channel使用的注意事项
(1)channel中只能存放指定的数据类型。
(2)channel的数据放满后,就不能再放入了。
(3)如果从channel取出数据后,可以继续放入。
(4)在没有使用协程的情况下,如果channel数据取完了,再取,就会报dead lock。
5、channel的遍历和关闭
![](https://img.haomeiwen.com/i6201627/b3dec84ee3c8fae9.png)
关闭例程如下:
![](https://img.haomeiwen.com/i6201627/62daf2a2298e32b6.png)
遍历例程如下:
![](https://img.haomeiwen.com/i6201627/f6e67d209dd87502.png)
三、channel的案例演示
1、创建一个intChan,最多可以存放3个int,演示存3个数据到intChan,然后再取出这3个int:
![](https://img.haomeiwen.com/i6201627/2e51ef7321d1ded8.png)
2、创建一个mapChan,最多可以存放10个map[string]string的键值对,演示写入和读取:
![](https://img.haomeiwen.com/i6201627/2c041574a7521d24.png)
3、创建一个catChan,最多可以存放10个Cat结构体变量,演示写入和读取的用法:
![](https://img.haomeiwen.com/i6201627/f42db6c065caa6ef.png)
5、创建一个allChan,最多可以存放10个任意数据类型变量,演示写入和读取的用法:
![](https://img.haomeiwen.com/i6201627/7605b41f2a8a3bb0.png)
四、管道的使用细节和注意事项
1、channel可以声明为只读或者只写性质
![](https://img.haomeiwen.com/i6201627/c67a8e170a6740a6.png)
![](https://img.haomeiwen.com/i6201627/e9fa272421674659.png)
2、channel只读和只写的最佳实践案例
![](https://img.haomeiwen.com/i6201627/57c728bdb0d2325e.png)
main方法中,有send和recv两个协程。send中需要穿恶毒管道ch参数,如果我们希望它是只写的,可以在定义send函数的时候写明参数ch chan<- int,这样ch在函数内就是只写而不能被读取了。这样的做法可以防止误操作。recv协程同理,我们希望管道ch是只读的,可以在定义recv函数的时候写明参数ch <-chan int。
3、使用select可以解决从管道取数据的阻塞问题。
![](https://img.haomeiwen.com/i6201627/30b13be31a670f27.png)
![](https://img.haomeiwen.com/i6201627/2c84184b6902c628.png)
![](https://img.haomeiwen.com/i6201627/c8a11170b2c35e2b.png)
4、goroutine中使用recover,解决协程中出现panic,导致程序崩溃问题。
如果我们起了一个协程,但是这个协程出现了panic,如果这个协程出现了panic,如果我们没有捕获panic,就会造成整个程序崩溃,这时我们可以在goroutine中使用recover来捕获panic,进行处理,这样即使这个协程发生了问题,主线程仍然不受影响,可以继续执行。
![](https://img.haomeiwen.com/i6201627/19b72a0c5bff5d7b.png)
这个示例中,画红线的这一行代码将来在执行时就会出错,因为map还没有用make分配空间就给它赋值了,最后test()协程会出错,进而造成整个主线程的崩溃停止运行。
![](https://img.haomeiwen.com/i6201627/8f6c6ad74808f342.png)
这里用defer和recover解决了这个问题,出错了,返回输出一个错误即可,整个主线程还在继续执行。
![](https://img.haomeiwen.com/i6201627/d7042209fac42985.png)
输出如下所示,虽然test()发生了错误,但是主线程依旧继续执行。
网友评论