Channel Patterns

Overview

Channels enable safe communication between goroutines. This chapter covers essential channel patterns.

Channel Directions

chan T     // Bidirectional
chan<- T   // Send-only
<-chan T   // Receive-only

func producer(out chan<- int) { }
func consumer(in <-chan int) { }

Buffered vs Unbuffered

// Unbuffered: send blocks until receive
ch := make(chan int)

// Buffered: send blocks when full
ch := make(chan int, 10)

Closing Channels

close(ch)

// Check if closed
v, ok := <-ch
if !ok {
    // Channel closed
}

// Range over channel
for v := range ch {
    // Receives until closed
}

Select

select {
case v := <-ch1:
    fmt.Println("from ch1:", v)
case v := <-ch2:
    fmt.Println("from ch2:", v)
case ch3 <- x:
    fmt.Println("sent to ch3")
default:
    fmt.Println("no channel ready")
}

Timeout Pattern

select {
case result := <-ch:
    fmt.Println(result)
case <-time.After(1 * time.Second):
    fmt.Println("timeout")
}

Done Channel

done := make(chan struct{})

go func() {
    // Work...
    close(done)  // Signal completion
}()

<-done  // Wait for signal

Fan-Out/Fan-In

// Fan-out: multiple goroutines read from one channel
func fanOut(in <-chan int, workers int) []<-chan int {
    outs := make([]<-chan int, workers)
    for i := 0; i < workers; i++ {
        outs[i] = worker(in)
    }
    return outs
}

// Fan-in: merge multiple channels into one
func fanIn(ins ...<-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    for _, in := range ins {
        wg.Add(1)
        go func(ch <-chan int) {
            defer wg.Done()
            for v := range ch {
                out <- v
            }
        }(in)
    }
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}

Summary

Pattern Use Case
Unbuffered Synchronization
Buffered Decouple speed
Select Multiple channels
Done channel Cancellation signal
Fan-out/in Parallel processing