【问题标题】:Checking if a channel has a ready-to-read value, using Go使用 Go 检查通道是否具有可读取的值
【发布时间】:2011-03-24 19:58:27
【问题描述】:

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

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

【问题讨论】:

    标签: go channel


    【解决方案1】:

    我所知道的从通道读取的唯一非阻塞操作是在 select block 内部,具有默认情况:

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

    try the non-blocking here

    注意以前的答案:接收操作符本身现在是阻塞操作,从 Go 1.0.3 开始。 spec 已被修改。请try the blocking here(死锁)

    【讨论】:

    • 在永久循环中使用时,可以与代码结合使用,以进行定期检查。自动收报机将防止紧密循环。
    • @AJPennster 好吧,默认阻塞行为的重点是消除各种忙循环的需要。如果可能,应避免 IMO 的“非阻塞读取”代码。而且,如果您在“非阻塞读取”周围的紧密循环甚至松散的滴答循环中旋转,那么您很可能使用它非常错误。更喜欢正确解耦的 goroutine,具有阻塞读取。
    【解决方案2】:

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

    【讨论】:

    • 这应该是正确的答案,我希望有更多的赞成票。您不应该有两个不同的工作流程,具体取决于频道是否有可用的东西。您可能会尝试用一个渠道做很多事情,需要重组您的计划,或者需要一个额外的渠道。
    【解决方案3】:

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

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

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

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

    【讨论】:

    • “但是它不能断定它可以从通道中读取而不会阻塞”——除非它知道它是唯一一个正在听/读的。
    【解决方案4】:

    很遗憾,之前的答案不正确。 spec 明确表示您可以使用 len() 函数以这种方式使用通道,但前提是您指定了通道容量 - 通道在制作时的缓冲区长度。如果您在制作时省略了通道容量 - 通道操作总是阻塞的。

    【讨论】:

    • 正在寻找两者的性能比较,因为我每秒要进行数千次检查。使用 len() 比使用 go 1.9.1 的 select{} 快​​大约 7 倍。 gist.github.com/phemmer/428685f5366c4b71b39d1037e6124a73
    • 你的len() 实现不会有竞争条件吗? IE。两个 goroutine 可以看到 len(chn) != 0,然后在运行 v := &lt;-chn 时阻塞,因为只有一个会真正获得值。
    • @JohnGibb - 如果读者数量已知,那么每个读者都可以避免阅读,直到所有读者都成功。 ;-) 如果只有一位读者,那么检查 != 0 就可以了。
    【解决方案5】:

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

    来自文档:

    如果接收表达式用于 赋值或初始化 表格

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

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

    【讨论】:

    • 警告! Receive 以前是这样工作的,但现在不再这样了。请参阅 Ripounet 的回答,最重要的是,检查您正在使用的 Go 版本的语言规范。
    • 支持更新您接受的答案,希望更多人这样做
    【解决方案6】:

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

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

    • 定义 hchan waitq sudog 结构,如此处定义 - https://golang.org/src/runtime/chan.go
    • 使用“不安全”包将通道转换为指向 hchan 结构的指针
    • 读取此结构的 sendq 字段以获取侦听器
    • first sudog 并从那里读取 msg 字段。
    • 使用“反射”和“不安全”将消息转换为适合通道的类型

    【讨论】:

    • 为什么这样做是一个糟糕的设计选择?为什么不应该遵循这些特定步骤?有没有更好的方法来实现可以解决问题的方法?
    • 因为从并发的角度来看,这引入了不可见的通道,该通道包含有关原始通道上存在的值的信息。出于讽刺的原因,不应遵循我所描述的步骤。更好的实现方式是显式创建另一个通道来保存有关原始通道的信息。
    • 您的答案中应该包含该信息,包括“正确”的做事方式。不应该遵循的讽刺答案不是好的答案。
    • @ArtemCo,“隐形通道”的概念很有趣,给了我一些思考。遗憾的是您没有将其作为您的答案的基础并详细说明。
    猜你喜欢
    • 2021-01-21
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 2013-06-23
    • 2016-04-23
    • 1970-01-01
    相关资源
    最近更新 更多