← Back to Dashboard

Day 2: Concurrency Patterns & Context

Select, Buffered Channels, and Context Propagation

1. Learn: Advanced Concurrency

Building on Day 1, we explore patterns to manage multiple goroutines.

2. Practice: Fan-Out / Fan-In with Context

Implement a pipeline that fans out work to multiple goroutines and fans results back in, with cancellation support.

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

func worker(ctx context.Context, id int, jobs <-chan int, results chan<- int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Worker %d stopping\n", id)
            return
        case j, ok := <-jobs:
            if !ok {
                return
            }
            fmt.Printf("Worker %d processing %d\n", id, j)
            time.Sleep(500 * time.Millisecond)
            results <- j * j
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    jobs := make(chan int, 10)
    results := make(chan int, 10)

    // Fan-Out
    for w := 1; w <= 3; w++ {
        go worker(ctx, w, jobs, results)
    }

    // Send jobs
    go func() {
        for j := 1; j <= 10; j++ {
            jobs <- j
        }
        close(jobs)
    }()

    // Fan-In (simplified for demo)
    // In production, use a separate goroutine + WaitGroup to close results
    for i := 0; i < 10; i++ {
        select {
        case res := <-results:
            fmt.Println("Result:", res)
        case <-ctx.Done():
            fmt.Println("Main: Context timeout!")
            return
        }
    }
}

3. Interview Questions

Q1: How do you prevent a Goroutine leak?

Answer: Ensure every goroutine has a defined exit condition. Common strategies include using a done channel, passing a context.Context that gets cancelled, or ensuring the channel it reads from is closed.

Q2: What is the difference between `context.Background()` and `context.TODO()`?

Answer: Functionally they are the same (empty contexts). Semantically, `Background()` is the root of a context tree (main, init, tests), while `TODO()` is a placeholder when you're unsure which context to use or it hasn't been passed down yet.

Q3: When should you use a Buffered Channel?

Answer: Use them when you want to decouple the sender and receiver to avoid blocking on every send, or to limit throughput (e.g., a semaphore). Be careful not to use them just to mask a race condition or deadlock.

4. Assessment

Progress Log