Select
The select
statement in Go is used to handle multiple channel operations concurrently
. It blocks until one of the operations can proceed. This is particularly useful when you have multiple goroutines communicating via channels and you want to handle their messages as they arrive
. You can also use the default
case in a select statement to prevent blocking
. If none of the cases are ready
, the default
case will be executed.
Example
We then use a select statement to receive data from either channel
. In this case, it will print the data received from the number channel because this channel is ready first
.
package main
import (
"fmt"
"time"
)
func main() {
number := make(chan int)
message := make(chan string)
go channelNumber(number)
go channelMessage(message)
select {
case firstChannel := <-number:
fmt.Println("Channel Data:", firstChannel)
case secondChannel := <-message:
fmt.Println("Channel Data:", secondChannel)
}
}
func channelNumber(number chan int) {
number <- 15
}
func channelMessage(message chan string) {
time.Sleep(2 * time.Second)
message <- "Learning Go Select"
}
Default
package main
import (
"fmt"
"log"
"time"
)
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
time.Sleep(2 * time.Second)
ch <- 1
}
}()
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
select {
case num := <-ch:
fmt.Printf("Number received: %d\n", num)
default:
log.Println("Default")
}
}
}
More Complex example
package main
import (
"fmt"
"log"
"sync/atomic"
"time"
)
type Message struct {
id int32
Msg string
}
var countId int32 = 0
func rabbitMQMessage(ch chan<- Message) {
// Infity loop
for {
atomic.AddInt32(&countId, 1)
msg := Message{countId, fmt.Sprintf("Message %d from RabbitMQ", countId)}
ch <- msg
if countId > 8 {
time.Sleep(4 * time.Second)
}
}
}
func kafkaMessage(ch chan<- Message) {
// Infity loop
for {
atomic.AddInt32(&countId, 1)
msg := Message{countId, fmt.Sprintf("Message %d from Kafka", countId)}
ch <- msg
if countId > 4 {
time.Sleep(4 * time.Second)
}
}
}
func main() {
ch1 := make(chan Message)
ch2 := make(chan Message)
go rabbitMQMessage(ch1)
go kafkaMessage(ch2)
for i := 0; i < 20; i++ {
select {
case msg := <-ch1:
fmt.Printf("Received from ch1: ID: %d - %s\n", msg.id, msg.Msg)
case msg := <-ch2:
fmt.Printf("Received from ch2: ID: %d - %s\n", msg.id, msg.Msg)
case <-time.After(time.Second * 2):
log.Print("timeout")
}
}
}
output
...
Received from ch2: ID: 1 - Message 1 from Kafka
Received from ch2: ID: 2 - Message 2 from Kafka
Received from ch2: ID: 4 - Message 4 from Kafka
Received from ch2: ID: 5 - Message 5 from Kafka
Received from ch1: ID: 5 - Message 3 from RabbitMQ
Received from ch1: ID: 6 - Message 6 from RabbitMQ
Received from ch1: ID: 7 - Message 7 from RabbitMQ
Received from ch1: ID: 8 - Message 8 from RabbitMQ
Received from ch1: ID: 9 - Message 9 from RabbitMQ
2023/12/20 12:05:49 timeout
2023/12/20 12:05:51 timeout
Received from ch1: ID: 10 - Message 10 from RabbitMQ
Received from ch2: ID: 11 - Message 11 from Kafka
2023/12/20 12:05:53 timeout
Received from ch2: ID: 12 - Message 12 from Kafka
Received from ch1: ID: 13 - Message 13 from RabbitMQ
2023/12/20 12:05:57 timeout
Received from ch1: ID: 14 - Message 14 from RabbitMQ
Received from ch2: ID: 15 - Message 15 from Kafka
2023/12/20 12:06:01 timeout