Home >Backend Development >Golang >Can You Select on Both Sending and Receiving Channels in Go, Without Blocking?
Selecting on Bidirectional Channels for Conditional Sending and Receiving
In Go, it's possible to set up buffered channels for both sending and receiving, allowing selective operations based on their availability. Consider the following scenario:
<code class="go">s := make(chan<- int, 5) r := make(<-chan int)
We have a buffered send channel s and an unbuffered receive channel r. The question arises: can we select on both channels to determine if r has data or s is not full? This mimics the functionality of the following, but without consuming 100% CPU:
<code class="go">for { if len(s) < cap(s) { // Send something } if len(r) > 0 { // Receive something } }</code>
Implementing Select with a Default Case
To achieve this, we can utilize the select statement with a default case. In the default case, if neither channel is ready, we temporarily "sleep" the goroutine to avoid unnecessary resource consumption.
<code class="go">s := make(chan<- int, 5) r := make(<-chan int) for { v := valueToSend() // Value to be sent, evaluated upon each attempt select { case s <- v: fmt.Println("Sent value:", v) case vr := <-r: fmt.Println("Received:", vr) default: // Neither channel is ready, pause briefly time.Sleep(time.Millisecond * 1) } }
Why Length and Capacity Checks Are Not Ideal
It's crucial to avoid checking the length or capacity of a channel before attempting to send or receive. This is because the channel's state can change between the time of checking and the actual operation, potentially leading to unexpected blocking. For instance:
<code class="go">if len(r) > 0 { // r is ready to receive // Other code... r <- // This may block if another goroutine has already received from r! }</code>
The above is the detailed content of Can You Select on Both Sending and Receiving Channels in Go, Without Blocking?. For more information, please follow other related articles on the PHP Chinese website!