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