使用Go检查通道是否具有准备读取的值

使用Go检查通道是否具有准备读取的值,go,channel,Go,Channel,如何检查通道是否有可供我读取的值 我不想在阅读频道时阻塞。我想看看它是否有价值。如果有,我会读的。如果它还没有,我会做些别的事情,稍后再检查。警告:这不再准确,请参阅下面的答案 从文档中: 如果接收表达式用于 分配或初始化 形式 x,ok=如果您经常这样做,那么这可能不是一个好的设计,您最好在频道没有任何内容可供阅读的情况下,生成另一个goroutine来完成您计划执行的任何工作。Go通道的同步/阻塞特性使代码更易于阅读和推理,而调度程序和廉价的goroutine意味着异步调用是不必要的,因为等

如何检查通道是否有可供我读取的值


我不想在阅读频道时阻塞。我想看看它是否有价值。如果有,我会读的。如果它还没有,我会做些别的事情,稍后再检查。

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

从文档中:

如果接收表达式用于 分配或初始化 形式


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

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

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

情况x,确定:=您没有,至少对于同步(无缓冲)通道没有。如果不要求从通道获取值,就无法判断值是否正在等待

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


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


出于您所描述的目的,请将select与Ripounet显示的默认大小写一起使用。

不幸的是,前面的答案不正确。说明中明确指出,您可以使用len()以这种方式使用频道函数,但仅当您指定了通道容量(创建通道时通道的缓冲区长度)。如果您在创建通道容量时忽略了通道容量,则通道操作总是阻塞的。

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

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

  • 定义hchanwaitqsudog此处定义的结构-
  • 使用“不安全”包将通道强制转换为指向hchan的指针
  • 读取此结构的sendq字段以获取侦听器
  • 首先读取sudog,然后从那里读取消息字段
  • 使用“反射”和“不安全”将消息转换为频道的适当类型

警告!Receive过去是这样工作的,但现在不再是了。请参阅Ripounet的答案,最重要的是,检查您正在使用的Go版本的语言规范。更新您接受的答案的道具,希望有更多的人这样做。这应该是正确的答案,我希望有更多的投票。您不应该有两个不同的工作流关于频道是否有可用的内容。您可能试图用一个频道做很多事情,需要重新构造程序,或者需要一个额外的频道。当在永久循环中使用时,这可以与计时器结合使用,以进行定期检查。计时器将防止出现紧循环。@Ajpenster嗯,默认阻塞行为的要点是s、 除其他外,消除各种繁忙循环的需要。如果可能,应避免使用“非阻塞读取”代码。如果在“非阻塞读取”周围的紧密循环甚至松散循环中旋转,则应避免使用“非阻塞读取”代码,那么您很可能使用它是非常错误的。更喜欢使用正确解耦的goroutines和阻塞读取。为什么这样做是一个糟糕的设计选择?为什么不应该遵循这些特定的步骤?有没有更好的方法来实现解决问题的方法?因为从并发性的角度来看,这会引入不可见的通道关于原始频道上存在值的保留信息。我所描述的步骤不应出于讽刺的原因而遵循。更好的实现是显式创建另一个频道保留原始频道的信息。该信息应包含在您的答案中,包括“正确的”做事的方式。不应该遵循的讽刺性答案不是好答案。@ArtemCo,“无形通道”的概念很有趣,给了我一些思考的东西。遗憾的是,你没有把它作为你答案的基础并详细说明。我正在寻找2的性能比较,因为我每秒要做数千次检查。使用
len()
比使用go 1.9.1的
select{}
快7倍。你的
len()
implementation有一个竞争条件?即两个goroutine可以看到
len(chn)!=0
,然后在运行
v:=@JohnGibb时阻塞-如果已知读卡器的数量,则每个读卡器都可以避免读取,直到所有读卡器都成功为止;-)对于一个读卡器,则检查!=0就可以了。“然而,它不能得出结论,它可以在不阻塞的情况下读取通道”——除非它知道它是唯一一个正在听/读的通道。
    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.")
    }