Flutter、Golang、Python、编译原理、算法、Chrome原理学习系列文章抢先看请关注【码农帮派】:
【Golang学习系列文章,请扫二维码】
先来看一段代码:
上面的代码中,数据的生产者和数据的接收者分别在两个协程中,通过Golang的CSP机制,通过channel发送/接收数据。
这段代码有一些问题:dataProducer和dataReceiver通过channel发送数据的时候,需要事先约定好发送/接收的数量,以此保证数据沟通的完整性,那么有没有更松的耦合方式呢?
我们可以约定一个特殊的数据标记(比如-1),当数据放完的时候,dataProducer就往channel中放入-1,表示数据已经发送完毕,但要是我们有好几个数据接收者,那么我们就需要放入很多个结束标记,这样显然也不友好。
最合适的方式是,当dataProducer发送完数据之后,关闭channel,而dataReceivers在从channel中获取数据的时候,同时判断channel的状态,要是channel已关闭,说明数据发送完毕。
dataProducer在发送完数据之后关闭channel:
对于消息接收者,要是channel被关闭,而消息接收者还在channel上接收数据,那么channel会立即返回channel消息类型的零值(上面的channel发送的int类型的消息,零值为0):
上面的代码中,dataProducer向channel中放入了5个消息之后就关闭了通道,而dataReceiver持续从channel中获取了7个消息,从打印结果可以看到,channel关闭之后,继续从channel读取消息程序并不会抛出异常,会立即返回通道消息的零值(int的零值为0)。
【注意】从已经关闭的channel读取数据并不会导致程序抛出异常,但是向已经关闭的channel中放入消息,会抛出panic异常。
dataReceivers在从channel中阻塞读取消息的时候,其实channel会返回两个数据,其中第二个数据可以用来判断channel当前时候处于关闭状态:
【注意】上面的代码中,ok是一个bool值,ok为true表示当前channel处于激活状态,ok为false的时候说明channel已经被关闭了,这时我们就没有必须继续监听channel的消息了。
【总结】
-
向已经关闭的channel中发送消息,会导致panic;
-
msg, ok <- ch; ok为bool值,ok为true表示正常接收,ok为false的时候,表示channel已经关闭;
-
所有channel接收者都会在channel关闭的时候,立刻从channel中返回上述的ok为false,此时的msg为消息数据类型的零值。这种机制常被用来向多个订阅者发送关闭消息。
下面利用channel的广播机制实现任务取消:
我们启动5个协程,协程内部通过channel发送消息,来判断什么时候取消当前协程任务:
上面的代码中,TestCancel中首先启动了5个协程,每个协程中每隔10毫秒判断一次是否应该取消协程任务,isCanceled方法中使用多路选择机制从channel中读取关闭消息,cancelChan一直处于阻塞状态,一旦有消息从cancelChan中发送,就会触发isCanceled函数返回true,是的协程任务取消。
接下来我们来看cancel函数中的方法如何完成。由上面的解析可知。cancelChan中只要有消息发出,任务就会被取消。
尝试在cancel函数中发送消息:
从上面的代码可以看到,只有一个协程被取消了,这是因为cancel函数中,向cancelChan发送了一个消息,可以被isCanceled函数消耗一次,取消掉一个协程的任务。要是我们要通知所有的协程取消任务,就需要知道总共有多少个协程,然后发送多少条消息,这样会导致代码耦合性大。
我们可以使用channel被关闭时,阻塞等待该channel的所有接收者都会立即返回的特点,进行多协程任务的取消:
通过close关闭cancelChan,实现了所有接听者的消息发送。