Golang中的Channel(一)

发布时间 2023-07-07 15:32:03作者: 易先讯

Golang在并发编程上有两大利器,分别是channel和goroutine。Golang中有一句名言:“使用通信来共享内存,而不是通过共享内存来通信”。这句话有两层意思,Go语言确实在sync包中提供了传统的锁机制,但更推荐使用channel来解决并发问题。这里先对Channel做一个基本的介绍,对于其深一层的实现原理,等今后有空了再学习总结一下。

 

u 什么是Channel

从字面上理解,channel的意思是管道。它是一种go协程用以接收或发送消息的安全的消息队列。channel就像两个go协程之间的导管,来实现各种资源的同步。如图示意:

https://user-gold-cdn.xitu.io/2019/12/8/16ee5c45b1dcb0ea?imageslim

Channel 是进程内的通信方式,因此通过 channel 传递对象的过程和调用函数时的参数传递行为比较一致,比如也可以传递指针等。使用channel发送和接收所需的共享资源,可以在 goroutine 之间消除竞争条件。Channel 是类型相关的,也就是说,一个 channel只能传递一种类型的值,这个类型需要在声明 channel 时指定。

 

u Channel的类型以及有无缓冲的channel

channel类型的定义格式如下:

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

它包括三种类型的定义。可选的<-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据:

    chan T          // 可以接收和发送类型为 T 的数据

    chan<- float64  // 只可以用来发送 float64 类型的数据

    <-chan int      // 只可以用来接收 int 类型的数据

       

        <-总是优先和最左边的类型结合:

        chan<- chan int    // 等价 chan<- (chan int)

    chan<- <-chan int  // 等价 chan<- (<-chan int)

    <-chan <-chan int  // 等价 <-chan (<-chan int)

    chan (<-chan int)

 

       使用make初始化Channel,并且可以设置容量:

       make(chan int, 100)

容量代表channel容纳的最多的元素的数量,代表Channel的缓存的大小。如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯才会发生。如果设置了缓存,就可能不发生阻塞,只有buffer满了后 send才会阻塞,而只有缓存空了后receive才会阻塞。一个nil channel不会通信。这就构成了所谓的Unbuffered Channels和Buffered Channels:

 

²  Unbuffered Channels

指缓冲区大小为0的channel,接收者会阻塞直至接收到消息,发送者会阻塞直至接收者接收到消息,这种机制可

用于两个goroutine进行状态同步。下图可以形象说明两个goroutine如何利用无缓冲的channel来共享

一个值(下图来自互联网):

 

 

²  Buffered Channels

指缓冲区大小不为0的channel,当缓冲区已满时,发送者会阻塞;当缓冲区为空时,接收者会阻塞。

这种类型的通道并不强制要求goroutine 之间必须同时完成发送和接收。这导致有缓冲的通道和无缓冲的通道之间的一

个很大的不同:无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的通道没有这种

保证。

同样可以以下图来展示: