Web Analytics

Select & Patterns

Advanced ~25 min read

The select statement lets you wait on multiple channel operations. It's like a switch for channels, enabling powerful concurrent patterns. In this lesson, you'll master select and learn professional concurrency patterns.

The Select Statement

Select waits on multiple channel operations:

Select Statement Channel 1 Channel 2 Channel 3 SELECT Waits for first ready channel select { case v := <-ch1:< /text> // Handle ch1 case v := <-ch2:< /text> // Handle ch2 case v := <-ch3:< /text> // Handle ch3 } Blocks until one case can proceed
Select waits for the first ready channel
Output
Click Run to execute your code
Select Behavior:
  • Waits until one case can proceed
  • If multiple cases ready, chooses randomly
  • Default case runs if no channel ready
  • Empty select blocks forever

Timeout Pattern

Use select with time.After for timeouts:

Output
Click Run to execute your code

Non-blocking Channel Operations

// Non-blocking receive
select {
case msg := <-ch:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message")
}

// Non-blocking send
select {
case ch <- msg:
    fmt.Println("Sent message")
default:
    fmt.Println("Channel full")
}

Common Concurrency Patterns

1. Done Channel Pattern

func worker(done <-chan bool) {
    for {
        select {
        case <-done:
            fmt.Println("Worker stopping")
            return
        default:
            // Do work
            fmt.Println("Working...")
            time.Sleep(time.Second)
        }
    }
}

func main() {
    done := make(chan bool)
    go worker(done)
    
    time.Sleep(3 * time.Second)
    done <- true  // Signal worker to stop
    time.Sleep(time.Second)
}

2. Ticker Pattern

func main() {
    ticker := time.NewTicker(500 * time.Millisecond)
    done := make(chan bool)
    
    go func() {
        for {
            select {
            case <-done:
                return
            case t := <-ticker.C:
                fmt.Println("Tick at", t)
            }
        }
    }()
    
    time.Sleep(2 * time.Second)
    ticker.Stop()
    done <- true
}

3. Context Pattern

Output
Click Run to execute your code
Best Practice: Use context.Context for cancellation and timeouts in production code. It's the standard way to manage goroutine lifecycles.

Common Mistakes

1. Forgetting default in non-blocking select

// ❌ Wrong - blocks if channel not ready
select {
case msg := <-ch:
    fmt.Println(msg)
}

// βœ… Correct - non-blocking
select {
case msg := <-ch:
    fmt.Println(msg)
default:
    fmt.Println("No message")
}

2. Not handling all channels in loop

// ❌ Wrong - done channel ignored in loop
for {
    select {
    case msg := <-messages:
        fmt.Println(msg)
    }
    // done channel never checked!
}

// βœ… Correct - check done channel
for {
    select {
    case <-done:
        return
    case msg := <-messages:
        fmt.Println(msg)
    }
}

Exercise: Rate Limiter

Task: Create a rate limiter using select and ticker.

Requirements:

  • Process requests at most once per second
  • Use time.Ticker for rate limiting
  • Handle 5 requests
  • Print when each request is processed
Show Solution
package main

import (
    "fmt"
    "time"
)

func main() {
    requests := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        requests <- i
    }
    close(requests)
    
    // Rate limiter: 1 request per second
    limiter := time.NewTicker(time.Second)
    defer limiter.Stop()
    
    fmt.Println("Rate Limiter: Processing requests...")
    fmt.Println("====================================")
    
    for req := range requests {
        <-limiter.C  // Wait for tick
        fmt.Printf("Request %d processed at %s\n", req, time.Now().Format("15:04:05"))
    }
    
    fmt.Println("====================================")
    fmt.Println("All requests processed!")
}

Summary

  • select waits on multiple channel operations
  • Random choice if multiple cases ready
  • default case makes select non-blocking
  • Timeout pattern with time.After
  • Done channel signals goroutine termination
  • Ticker for periodic operations
  • Context for cancellation and timeouts

What's Next?

Congratulations on completing the Concurrency module! You've mastered goroutines, channels, and selectβ€”Go's most powerful features. Next, you'll explore Packages & Standard Library to build real-world applications!