为什么不能用len来判满或判空

为什么不能用len来判满或判空

Content #

针对 channel ch 的类型不同,len(ch) 有如下两种语义:

  1. 当 ch 为无缓冲 channel 时,len(ch) 总是返回 0;
  2. 当 ch 为带缓冲 channel 时,len(ch) 返回当前 channel ch 中尚未被读取的元素个数。

这样一来,针对带缓冲 channel 的 len 调用似乎才是有意义的。那我们是否可以使用 len 函数来实现带缓冲 channel 的“判满”、“判有”和“判空”逻辑呢?就像下面示例中伪代码这样:

var ch chan T = make(chan T, capacity)

// 判空
if len(ch) == 0 {
    // 此时channel ch空了?
}

// 判有
if len(ch) > 0 {
    // 此时channel ch中有数据?
}

// 判满
if len(ch) == cap(ch) {
    // 此时channel ch满了?
}

channel 原语用于多个 Goroutine 间的通信,一旦多个 Goroutine 共同对 channel 进行收发操作,len(channel) 就会在多个 Goroutine 间形成“竞态”。单纯地依靠 len(channel) 来判断 channel 中元素状态,是不能保证在后续对 channel 的收发时 channel 状态是不变的。

我们以判空为例看看:

Goroutine1 使用 len(channel) 判空后,就会尝试从 channel 中接收数据。但在它真正从 channel 读数据之前,另外一个 Goroutine2 已经将数据读了出去,所以,Goroutine1 后面的读取就会阻塞在 channel 上,导致后面逻辑的失效。

Viewpoint #

From #

33|并发:小channel中蕴含大智慧