有没有可靠的方法来确保 Go 通道在读取时不会阻塞?

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

Is there a reliable way to ensure a Go channel does not block on read?

问题

这是对之前一个类似名称的帖子的跟进。

它有一个被接受的答案,但那个答案并没有真正回答问题。从那个帖子中,这是使用案例:

if len(myChannel) > 0 {
   // 可能存在问题:长度可能已经变为0,使得这个操作变为阻塞的
   elm := <- myChannel
   return elm
}

原帖的提问者称其为“可能存在问题”,但这是一个明确的问题:在评估if条件和执行这两个语句之间,另一个消费者可能已经从通道中取走了一个值,导致竞态条件。

现在,我们被告知Go语言的方式是优先使用通道而不是互斥锁,但在这种情况下,似乎我们无法实现基本的非阻塞读取(通过轮询长度并进行原子读取)而不将互斥锁和通道配对使用,并使用我们的新并发数据类型代替通道。

这样做是正确的吗?难道真的没有可靠的方法来确保接收操作不会阻塞,而是提前检查空间吗?(与Java中的BlockingQueue.poll()或其他基于队列的消息传递IPC工具类似...)

英文:

This is a followup to a previous thread with a similar name.

It has an accepted answer, but that answer does not really answer the question. From that thread, here is the use-case:

if len(myChannel) &gt; 0 {
   // Possible issue here: length could have changed to 0 making this blocking
   elm := &lt;- myChannel
   return elm
 }

The OP calls it a "Possible issue", but it's a Definite Issue: a race condition in which another consumer may have pulled a value from the channel between the evaluation of the if condition and execution of the two statements.

Now, we are told the Go Way is to favor channels over mutex, but here it seems we can not acheive even basic non-blocking read (by polling length and reading atomically) without pairing a mutex and a channel together, and using our new concurrency data type instead of a channel.

Can that be right? Is there really no way to reliably ensure a recv does not block by checking ahead for space? (Compare with BlockingQueue.poll() in Java, or similar facilities in other queue-based messaging IPC facilities...)

答案1

得分: 9

这正是select语句中的默认情况所用的:

var elm myType
select {
case elm = <-myChannel:
default:
}
return elm

如果可以的话,它会将值赋给elm,否则返回零值。在Effective Go的"泄漏缓冲区"一节中有一个更详细的示例。

英文:

This is exactly what default cases in select are for:

var elm myType
select {
case elm = &lt;-myChannel:
default:
}
return elm

This assigns elm if it can, and otherwise returns a zero value. See "A leaky buffer" from Effective Go for a somewhat more extensive example.

答案2

得分: 2

Rob Napier的回答是正确的。

然而,你可能过于努力地追求非阻塞行为,认为这是一种反模式。

在Go中,你不需要担心阻塞。放心地阻塞吧,这样可以让代码编写得更容易,特别是在处理输入/输出时。

CSP允许你设计数据驱动的并发程序,可以非常好地扩展(因为不过多使用互斥锁)。通过通道进行通信的小组goroutine可以像较大系统的组件一样运行;这些组件(也通过通道进行通信)可以组合成更大的组件;这种模式在不断扩大的规模上重复。

传统上,人们从顺序代码开始,然后通过添加goroutine、通道、互斥锁等来尝试添加并发性。作为一种练习,尝试一些不同的方法:尽可能地设计一个最大并发的系统-尽可能深入地使用goroutine和通道。你可能对你所实现的性能不满意...然后也许尝试考虑如何通过组合(而不是分割)块来改进它,减少总的goroutine数量,从而实现更优化的并发性。

英文:

Rob Napier's answer is correct.

However, you are possibly trying too hard to achieve non-blocking behaviour, assuming that it is an anti-pattern.

With Go, you don't have to worry about blocking. Go ahead, block without guilt. It can make code much easier to write, especially when dealing with i/o.

CSP allows you to design data-driven concurrent programs that can scale very well (because of not using mutexes too much). Small groups of goroutines communicating via channels can behave like a component of a larger system; these components (also communicating via channels) can be grouped into larger components; this pattern repeats at increasing scales.

Conventionally, people start with sequential code and then try to add concurrency by adding goroutines, channels, mutexes etc. As an exercise, try something different: try designing a system to be maximally concurrent - use goroutines and channels as deeply as you possibly can. You might be unimpressed with the performance you achieve ... so then perhaps try to consider how to improve it by combining (rather than dividing) blocks, reducing the total number of goroutines and so achieving a more optimal concurrency.

huangapple
  • 本文由 发表于 2015年4月29日 08:36:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/29932360.html
匿名

发表评论

匿名网友

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

确定