检查通道是否有准备好的可读取值,使用Go语言。

huangapple go评论78阅读模式
英文:

Checking if a channel has a ready-to-read value, using Go

问题

我如何检查一个通道是否有值可以读取?

我不想在读取通道时被阻塞。我想要查看它是否有值。如果有值,我会读取它。如果没有值(尚未有值),我会做其他事情,稍后再次检查。

英文:

How do I check whether a channel has a value for me to read?

I don't want to block when reading a channel. I want to see whether it has a value. If it does have one, I'll read it. If it doesn't have one (yet), I'll do something else and check back again later.

答案1

得分: 129

我知道的唯一非阻塞从通道读取的操作是在一个select块中使用默认情况:

	select {
	case x, ok := <-ch:
		if ok {
			fmt.Printf("读取到值 %d。\n", x)
		} else {
			fmt.Println("通道已关闭!")
		}
	default:
		fmt.Println("没有准备好的值,继续执行。")
	}

请在这里尝试非阻塞操作

关于之前的答案的说明:从Go 1.0.3版本开始,接收操作本身一个阻塞操作。规范已经进行了修改。请在这里尝试阻塞操作(死锁)

英文:

The only non-blocking operation I know of to read from a channel is inside a select block having a default case :

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

Please try the non-blocking here

Note about previous answers: the receive operator itself is now a blocking operation, as of Go 1.0.3 . The spec has been modified. Please try the blocking here (deadlock)

答案2

得分: 15

很抱歉,之前的回答是错误的。规范明确指出,你可以使用len()函数这种方式来使用通道,但前提是在创建通道时指定了通道容量 - 即通道的缓冲区长度。如果在创建通道时没有指定通道容量,通道操作将始终阻塞。

英文:

Unfortunately, the previous answers are incorrect. The spec clearly says that you CAN use channels this way using len() function, but only if you specified the channel capacity - the buffer length for a channel while making it. If you omitted a channel capacity while making it - the channel operations are always blocking.

答案3

得分: 14

如果你经常这样做,那么这可能不是一个很好的设计,你最好创建另一个goroutine来执行当通道中没有可读取内容时要执行的工作。Go语言通道的同步/阻塞特性使得代码更易于阅读和理解,而调度器和廉价的goroutine意味着异步调用是不必要的,因为等待的goroutine占用的资源很少。

英文:

If you're doing this often then it's probably not a great design and you might be better off spawning another goroutine to do whatever work you're planning to do when there isn't anything to read from the channel. The synchronous/blocking nature of Go's channels make code easier to read and reason about while the scheduler and cheap goroutines means that async calls are unnecessary since waiting goroutines take up very little resources.

答案4

得分: 10

你不能,至少对于同步(非缓冲)通道来说是不行的。没有办法在不从通道中取值的情况下知道是否有值在等待。

对于缓冲通道来说,你在技术上可以使用len函数来实现你描述的功能,但你真的不应该这样做。你的技术是无效的。

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

对于你描述的目的,可以使用带有默认情况的select语句,就像Ripounet展示的那样。

英文:

You don't, at least not for synchronous (unbuffered) channels. There is no way to tell if a value is waiting without asking to take the value from the channel.

For buffered channels, you technically can use the len function to do what you describe, but you really, really shouldn't. Your technique is invalid.

The reason is that it represents a race condition. Given channel ch, your goroutine might see that len(ch) > 0 and conclude that there is a value waiting. It cannot conclude however, that it can read from the channel without blocking--another goroutine might empty the channel between the time you check len and the time your receive operation runs.

For the purpose you described, use select with a default case as Ripounet showed.

答案5

得分: 7

警告:此内容已不再准确,请参阅下面的答案。

从文档中可以看到:

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

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

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

英文:

WARNING: This is no longer accurate, see the answer below.

<s>From the docs:

> If a receive expression is used in an
> assignment or initialization of the
> form
>
> x, ok = <-ch
> x, ok := <-ch
> var x, ok = <-ch
>
> the receive operation becomes
> non-blocking. If the operation can
> proceed, the boolean variable ok will
> be set to true and the value stored in
> x; otherwise ok is set to false and x
> is set to the zero value for its type
</s>

答案6

得分: -4

在大多数情况下,依赖这样的信息是一个非常糟糕的设计选择。更不用说它在实现上是多么的混乱了。

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

  • 定义如下所示的hchanwaitqsudog结构 - https://golang.org/src/runtime/chan.go
  • 使用"unsafe"包将通道转换为指向hchan结构的指针
  • 读取该结构的sendq字段以获取监听器
  • 读取first sudog并从中读取msg字段。
  • 使用"reflect"和"unsafe"将msg转换为适当类型的通道。
英文:

In most cases relying on such information is a really bad design choice. Not even saying about how it's dirty in it's implementation.

So, do not implement the following steps to detect if channel is ready for read at runtime:

  • define hchan waitq sudog structs as defined here - https://golang.org/src/runtime/chan.go
  • use "unsafe" package to cast channel to pointer to hchan struct
  • read sendq field of this struct to get listeners
  • read first sudog and read msg field from there.
  • cast msg to the appropriate type for the channels using "reflect" and "unsafe"

huangapple
  • 本文由 发表于 2010年8月4日 00:17:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/3398490.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定