How to know a buffered channel is full? I don't know to be blocked when the buffered channel is full, instead I choose to drop the item sent to the buffered channel.
-
The premise of the question is that you want to avoid the channel ever becoming full. But the fact that channels *can* synchronise and therefore block either end is an important part of CSP thinking. Don't try too hard to prevent your buffers filling up until you understand synchronisation more fully. As an exercise, try solving several problems using *unbuffered* channels only. Then you can see how adding buffering afterwards can be a performance improvement to an already working system. (There are occasionally cases where too much buffering might even reduce performance.) – Rick-777 Sep 04 '14 at 21:43
4 Answers
You can use the select
statement with a default. In case it is not possible to do any of the cases, like sending to a full channel, the statement will do the default:
package main
import "fmt"
func main() {
ch := make(chan int, 1)
// Fill it up
ch <- 1
select {
case ch <- 2: // Put 2 in the channel unless it is full
default:
fmt.Println("Channel full. Discarding value")
}
}
Output:
Channel full. Discarding value
Playground: http://play.golang.org/p/1QOLbj2Kz2
Check without sending
It is also possible to check the number of elements queued in a channel by using len(ch)
, as stated in the Go specifications.
This in combination with cap
allows us to check if a channel is full without sending any data.
if len(ch) == cap(ch) {
// Channel was full, but might not be by now
} else {
// Channel wasn't full, but might be by now
}
Note that the result of the comparison may be invalid by the time you enter the if
block
-
2But what if one only wants to check if the buffer is full, _not_ writing to it even when it's not? Is there a way to do that? – Tom Oct 16 '15 at 10:36
-
22@Tom You can actually test `if len(ch) == cap(ch) { ... }` where `len(ch)` is the number of items in the channel and `cap(ch)` is the capacity. However, it might be invalid by the time you enter the if block. – ANisus Oct 16 '15 at 11:11
-
Thanks for clarifying that the result of the if statement may be invalid by the time the block is entered - I added it to the original answer. – NSTJ Feb 25 '17 at 04:51
-
2If the channel is full and your select hits ```default```, it might not be full afterwards; depending on your usage, ```len(ch) == cap(ch)``` can be just as useful – Alex Sep 05 '17 at 20:45
instead I choose to drop the item sent to the buffered channel.
That is called "overflowing channel", and you find ANisus's answer implemented in eapache/channels/overflowing_channel.go
:
for elem := range ch.input {
// if we can't write it immediately, drop it and move on
select {
case ch.output <- elem:
default:
}
}
close(ch.output)
But that project eapache/channels implements other strategies as well:
OverflowingChannel
implements theChannel
interface in a way that never blocks the writer.
Specifically, if a value is written to anOverflowingChannel
when its buffer is full
(or, in an unbuffered case, when the recipient is not ready) then that value is simply discarded.
For the opposite behaviour (discarding the oldest element, not the newest) see
RingChannel
.

- 1,262,500
- 529
- 4,410
- 5,250
-
2A good answer. Overflowing channels which lose either the oldest or the newest item can sometime be vital tools in your toolbox. Consider a ring of goroutines: normally, any loop (a.k.a *cycle*) gives a risk of deadlock. Changing any one of the channels to be an overflowing one fixes this problem. Losing some events doesn't matter if they're the kind that can become stale can easily be replaced. There are (of course) other ways to fix the same deadlock problem. – Rick-777 Sep 04 '14 at 21:39
Another useful example I stumbled upon was this nifty implementation of Ring Buffer.
The quote from the source:
The idea is simple: Connect two buffered channels through one Goroutine that forwards messages from the incoming channel to the outgoing channel. Whenever a new message can not be placed on on the outgoing channel, take one message out of the outgoing channel (that is the oldest message in the buffer), drop it, and place the new message in the newly freed up outgoing channel.
Check out this C version as well...

- 4,623
- 3
- 44
- 48
I use this code to remove one item if the channel was full. For channels with only one sending go routine it's enough to ensure that sending to ch
will work afterwards.
// Remove one item from chan if full
if len(ch) == cap(ch) {
// Channel was full, but might not be by now
select {
case _ := <-ch:
// Discard one item
default:
// Maybe it was empty already
}
}
// Now we can send to channel

- 16,283
- 13
- 71
- 107