BadZen
BadZen

Reputation: 4273

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

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) > 0 {
   // Possible issue here: length could have changed to 0 making this blocking
   elm := <- 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...)

Upvotes: 0

Views: 717

Answers (2)

Rick-777
Rick-777

Reputation: 10248

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.

Upvotes: 2

Rob Napier
Rob Napier

Reputation: 299355

This is exactly what default cases in select are for:

var elm myType
select {
case elm = <-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.

Upvotes: 9

Related Questions