浅谈Go语言并发基础之channel

154 阅读2分钟

今天我想和大家分享一下Go语言的Channel(我喜欢称之为信道:即通信管道)。

Goroutine和Channel并称为Go语言并发编程的两大基石。Go有一句很经典的话:那就是Do not communicate by sharing memory instead,share memory by communicate,即:不要通过共享内存进行通信,而是通过通信来共享内存。

Channel是go语言所特有的一个通信机制,一个Channel是一个通信机制。在go语言中,每一个并发的执行单元叫做一个Goroutine,而Channel是协程之间的通信管道,可以传递或者接受来自其他Goroutine的信息。在Go语言中,我们在大多数情况下都是使用channel来解决并发过程中的数据竞争问题,而不使用锁,因为Channel是并发安全的。

如何安全的关闭Channel?

关于Channel,有一些使用要点:

很多初学者朋友在使用channel时,不知道如何判断channel是否已经处于关闭状态,从而盲目关闭channel或者向已关闭的channel中写入数据或者读取数据。我们在不改变channel自身状态的情况下,无法的得知channel是否关闭,如果我们关闭一个已关闭的channel或者向一个已关闭的channel中写入数据会导致panic,这是一个很危险的举动。

对于关闭channel,有比较安全的关闭方法。

1.使用defer-recover可以放心的关闭channel,因为即使是发生了panic,也会有defer-recover来捕获这一panic。

2.使用 sync.Once 。

如何优雅的关闭Channel?

情况较为复杂,需要根据发送者和接收者的个数来分情况。(仅在此列举常用的两种情况)

情况一:当只有一个发送者一个接收者或者只有一个发送者多个接收者时,我们可以直接从发送端关闭,正如上面生产消费的代码例子:只需要从生产方关闭即可。

情况二:多个发送者和多个接收者。这种情况下我们需要增加一个传递信息的Achannel,这个Achannel是用来提醒发送者何时停止发送。即:我们通过接收者去关闭这个Achannel,将关闭的信息存入Achannel的缓冲区,然后告知发送者即可。通常在发送者模块使用select分支结构来判断,即:case <- Achannel :return 即可,因为此时若Achannel中有关闭信息,则会被读取到。