42

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

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

4

6 回答 6

114

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

    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.")
    }

在此处尝试非阻塞

请注意以前的答案:从 Go 1.0.3 开始,接收运算符本身现在一个阻塞操作。规范已修改。请在这里尝试阻塞(死锁)

于 2012-12-30T17:27:32.743 回答
12

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

于 2010-08-27T18:32:30.700 回答
10

您不会,至少对于同步(无缓冲)通道而言不会。如果不要求从通道中获取值,就无法判断一个值是否正在等待。

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

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

出于您描述的目的,将 select 与 Ripounet 所示的默认情况一起使用。

于 2013-01-02T19:27:16.130 回答
9

不幸的是,以前的答案是不正确的。规范清楚地表明您可以使用 len() 函数以这种方式使用通道,但前提是您指定了通道容量 - 通道在制作时的缓冲区长度。如果您在制作时省略了通道容量 - 通道操作始终是阻塞的。

于 2016-03-09T21:48:53.307 回答
6

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

从文档:

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

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch

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

于 2010-08-03T16:24:25.393 回答
-3

在大多数情况下,依赖这些信息是一个非常糟糕的设计选择。甚至没有说它的实现有多脏。

因此,不要执行以下步骤来检测通道是否已准备好在运行时读取:

  • 定义此处定义的 hchan waitq sudog结构 - https://golang.org/src/runtime/chan.go
  • 使用“不安全”包将通道转换为指向hchan结构的指针
  • 读取此结构的sendq字段以获取侦听器
  • 读取第一个 sudog并从那里读取 msg 字段。
  • 使用“反射”和“不安全”将 msg 转换为通道的适当类型
于 2017-11-28T14:05:07.950 回答