This is an old revision of the document!
Table of Contents
Go select with Non-blocking Send and Receive
Overview
In Go, the select statement allows a goroutine to wait on multiple channel operations. When combined with a default case, select can be used to perform non-blocking send and receive operations on channels.
📌 non-blocking /nɒn ˈblɒk.ɪŋ/ → does not wait; continues immediately
Basic Concept
Normally, sending to or receiving from a channel may block if:
the channel is full (send)
the channel is empty (receive)
no goroutine is ready on the other side
Using select with default avoids blocking by trying once and moving on.
📌 block /blɒk/ → pause execution and wait
Non-blocking Send
A non-blocking send attempts to send a value on a channel without waiting.
Syntax
select {
case ch <- value: // send succeeded
default: // send would block, so skip
}
Explanation
If the channel can accept the value:
the send happens immediately
If the channel cannot accept the value:
the default case runs
the goroutine continues execution
📌 attempt /əˈtempt/ → try to do something
📌 accept /əkˈsept/ → receive or take in
Non-blocking Receive
A non-blocking receive attempts to receive a value only if one is available.
Syntax
select {
case value := <-ch: // receive succeeded
default: // no value available
}
Explanation
If a value is available:
it is received immediately
If no value is available:
default executes
execution continues without waiting
📌 receive /rɪˈsiːv/ → get or take something
📌 available /əˈveɪ.lə.bəl/ → ready to be used
Buffered vs Unbuffered Channels
Unbuffered Channel
ch := make(chan int)
Send and receive must happen at the same time
Non-blocking send will fail if no receiver is ready
Used for synchronization
📌 synchronization /ˌsɪŋ.krə.naɪˈzeɪ.ʃən/ → coordinating actions to happen together
Buffered Channel
ch := make(chan int, 2)
Channel can hold values temporarily
Non-blocking send succeeds if buffer is not full
Non-blocking receive succeeds if buffer is not empty
📌 buffer /ˈbʌf.ər/ → temporary storage area
Common Mistakes
Ignoring the result of append-like behavior
select {
case ch <- v:
default:
}
If the send fails, the value is silently dropped.
📌 silently /ˈsaɪ.lənt.li/ → without any warning or message
Using len(ch) to decide send/receive
This is unsafe:
if len(ch) < cap(ch) { ch <- v // race condition }
📌 race condition /reɪs kənˈdɪʃ.ən/ → bug caused by timing between goroutines
Always prefer select.
When to Use Non-blocking Channel Operations
Use non-blocking send/receive when:
you want to avoid deadlocks
dropping data is acceptable
implementing polling or event loops
building soft real-time systems
📌 deadlock /ˈded.lɒk/ → program stops because goroutines wait on each other
📌 polling /ˈpəʊ.lɪŋ/ → checking repeatedly
Key Takeaways
select + default enables non-blocking behavior
Non-blocking send = try to send once, never wait
Non-blocking receive = try to receive once, never wait
Works best with buffered channels
Safe and idiomatic Go concurrency pattern
