Open In App

Select Statement in Go Language

Last Updated : 06 Nov, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In Go, the select statement allows you to wait on multiple channel operations, such as sending or receiving values. Similar to a switch statement, select enables you to proceed with whichever channel case is ready, making it ideal for handling asynchronous tasks efficiently.

Example

Consider a scenario where two tasks complete at different times. We’ll use select to receive data from whichever task finishes first.

Go
package main
import (
	"fmt"
	"time"
)
func task1(ch chan string) {
	time.Sleep(2 * time.Second)
	ch <- "Task 1 completed"
}

func task2(ch chan string) {
	time.Sleep(4 * time.Second)
	ch <- "Task 2 completed"
}

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go task1(ch1)
	go task2(ch2)

	select {
	case msg1 := <-ch1:
		fmt.Println(msg1)
	case msg2 := <-ch2:
		fmt.Println(msg2)
	}
}

Output
Task 1 completed

Note: After 2 seconds, Task 1 completed will be printed because task1 completes before task2. If task2 finished first, then Task 2 completed would be printed.

Syntax

The select statement in Go listens to multiple channel operations and proceeds with the first ready case.

select {
    case value := <-channel1:
        // Executes if channel1 is ready to send/receive
    case channel2 <- value:
        // Executes if channel2 is ready to send/receive
    default:
        // Executes if no other case is ready
}

Key Points:

  1. select waits until at least one channel operation is ready.
  2. If multiple cases are ready, one is chosen at random.
  3. The default case executes if no other case is ready, avoiding a block.

Select Statement Variations

Basic Blocking Behavior

In this variation, we modify the earlier example by removing the select statement and observe blocking behavior when no channels are ready.

Example

Go
package main
import (
	"fmt"
)
func main() {
	ch := make(chan string)

	select {
	case msg := <-ch:
		fmt.Println(msg)
	default:
		fmt.Println("No channels are ready")
	}
}

Output
No channels are ready

Explanation: Here, the default case ensures that select doesn’t block if no channels are ready, printing "No channels are ready".

Handling Multiple Cases

If both tasks are ready at the same time, select chooses one case randomly. This can happen if tasks have similar sleep times.

Example

Go
package main 
import (
    "fmt"
    "time"
)
func portal1(channel1 chan string) {
    time.Sleep(3 * time.Second)
    channel1 <- "Welcome to channel 1"
}
func portal2(channel2 chan string) {
    time.Sleep(9 * time.Second)
    channel2 <- "Welcome to channel 2"
}
func main() {
    R1 := make(chan string)
    R2 := make(chan string)
    // calling function 1 and function 2 in goroutine
    go portal1(R1)
    go portal2(R2)
    select {
    // case 1 for portal 1
    case op1 := <-R1:
        fmt.Println(op1)
    // case 2 for portal 2
    case op2 := <-R2:
        fmt.Println(op2)
    }
}

Output
Welcome to channel 1

Explanation: In this scenario, select randomly picks one of the two cases if both are ready at the same time, meaning you may see "Task 1 completed" or "Task 2 completed" depending on the selection.

Using Select with Default Case to Avoid Blocking

Using default ensures the program does not block if no case is ready. Here’s an example modifying the base structure to include a default case.

Example

Go
package main
import (
	"fmt"
)
func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	select {
	case msg1 := <-ch1:
		fmt.Println(msg1)
	case msg2 := <-ch2:
		fmt.Println(msg2)
	default:
		fmt.Println("No tasks are ready yet")
	}
}

Output
No tasks are ready yet

Explanation: Since neither ch1 nor ch2 is ready, the default case executes, outputting "No tasks are ready yet".

Infinite Blocking without Cases

If a select statement is empty (i.e., contains no cases), it blocks indefinitely. This is often used in cases where an infinite wait is necessary, but here’s what it looks like using our channels.

Go
package main

func main() {
	select {}  // This blocks forever as there are no cases
}

Explanation: Since there are no cases, select will block permanently, causing a deadlock if there are no other goroutines active.



Next Article

Similar Reads