使用Go检查通道是否具有可读取值

gon*_*bgo 30 channel go

如何检查频道是否有值供我阅读?

我不想在阅读频道时阻止.我想看看它是否有价值.如果它有一个,我会读它.如果它还没有(还),我会做其他事情,稍后再回来查看.

谢谢!

Del*_*ace 86

我知道从通道读取的唯一非阻塞操作是在具有默认情况的选择块内:

    select {
    case x, ok := <-ch:
        if ok {
            fmt.Printf("Value %d was read.\n", x)
        } else {
            fmt.Println("Channel closed!")
        }
    default:
        fmt.Println("No value ready, moving on.")
    }
Run Code Online (Sandbox Code Playgroud)

在这里尝试非阻止

备注以前的答案:接收运营商本身就是现在阻塞操作,如围棋1.0.3.该规范已被修改.请在这里尝试阻止(死锁)

  • @AJPennster 嗯,除其他外,默认阻塞行为的重点是消除对各种忙循环的需要。如果可能,应避免 IMO 使用“非阻塞读取”代码。如果您在“非阻塞读取”周围以紧密的循环甚至松散的滴答循环旋转,那么您很可能使用它是非常错误的。更喜欢正确解耦的 goroutines,阻塞读取。 (2认同)

Jes*_*sta 8

如果你经常这样做,那么它可能不是一个很棒的设计,如果没有任何东西可以从频道中读取,你可能最好再产生另一个goroutine去做你计划做的任何工作.Go的通道的同步/阻塞特性使代码更容易阅读和推理,而调度程序和廉价的goroutine意味着异步调用是不必要的,因为等待goroutines占用非常少的资源.


Son*_*nia 7

你没有,至少不是同步(无缓冲)通道.如果没有要求从通道中获取值,则无法判断值是否在等待.

对于缓冲通道,从技术上讲,您可以使用len函数来执行您所描述的操作,但实际上,您确实不应该这样做.你的技术无效.

原因是它代表了竞争条件.给定通道ch,你的goroutine可能会看到len(ch)> 0并得出结论是有值等待.但是,它无法得出结论,它可以在不阻塞的情况下从通道读取 - 另一个goroutine可能会在您检查len和接收操作运行的时间之间清空通道.

出于您所描述的目的,使用select作为Ripounet显示的默认情况.

  • “然而,它不能得出结论,它可以在不阻塞的情况下从通道中读取”——除非它知道它是唯一一个正在监听/读取的通道。 (3认同)

nos*_*nos 5

警告:这不再准确,请参阅下面的答案.

来自文档:

如果在表单的赋值或初始化中使用了接收表达式

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
Run Code Online (Sandbox Code Playgroud)

接收操作变为非阻塞.如果操作可以继续,则布尔变量ok将被设置为true并且值存储在x中; 否则ok设置为false,x设置为其类型的零值

  • 警告!接收曾经像这样工作,但它不再.请参阅Ripounet的答案,最重要的是,检查您正在使用的Go版本的语言规范. (8认同)
  • 更新您接受的答案的道具,希望更多人这样做 (4认同)

Yar*_*ych 5

不幸的是,先前的答案是错误的。该规范明确地说,你可以使用信道使用LEN()函数这种方式,但前提是你指定的信道容量-缓冲区长度的通道,而使得它。如果在创建通道容量时忽略了该容量,则通道操作始终处于阻塞状态。

  • 您的`len()`实现是否存在竞争条件?IE的两个goroutine可能会看到`len(chn)!= 0`,然后在运行`v:= &lt;-chn`时阻塞,因为只有一个实际会得到该值。 (2认同)