Channels

A channel is a typed "pipe" through which goroutines pass values to each other. The <- operator is used for both sending and receiving.

Unbuffered channel

A send blocks until another goroutine receives the value:

ch := make(chan int)

go func() {
    ch <- 42 // send
}()

v := <-ch // receive
fmt.Println(v)

Buffered channel

A send blocks only when the buffer is full:

ch := make(chan string, 2)
ch <- "a"
ch <- "b"
fmt.Println(<-ch, <-ch)

Closing and range

func gen(n int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for i := 0; i < n; i++ {
            out <- i
        }
    }()
    return out
}

for v := range gen(5) {
    fmt.Println(v)
}

Only the sender should close a channel. Receiving from a closed channel returns the zero value; you can check the state like this: v, ok := <-ch.

select

select waits until one of several channels is ready:

select {
case msg := <-ch1:
    fmt.Println("from ch1:", msg)
case msg := <-ch2:
    fmt.Println("from ch2:", msg)
case <-time.After(time.Second):
    fmt.Println("timeout")
}

Worker pool

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        results <- j * j
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    for i := 0; i < 5; i++ {
        fmt.Println(<-results)
    }
}

Directional channels

This helps the compiler catch mistakes and makes APIs clearer.