SkillByAIOpen interactive version →

Lesson 15 / 26

Channels and Pipelines

Communicate by sending values.

Unbuffered, buffered, closed

A channel passes values between goroutines. An unbuffered channel synchronises sender and receiver; a buffered channel (make(chan T, n)) lets sends proceed until the buffer is full. The sender closes a channel to signal no more values, and for v := range ch stops when it is closed. Directional types (<-chan T, chan<- T) document who sends and who receives. Chaining stages through channels builds pipelines.

A two-stage pipeline and a buffered channel, run

I ran this with Go 1.27.1 (go run . in a module named demo, standard library only). A producer sends 1 to 5, a square stage squares them, and main sums them to 55. The buffered channel holds two values without a receiver, then both are read.

package main

import "fmt"

func producer(n int) <-chan int {
	ch := make(chan int)
	go func() {
		defer close(ch) // closing tells receivers no more values are coming
		for i := 1; i <= n; i++ {
			ch <- i
		}
	}()
	return ch
}

func square(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for v := range in {
			out <- v * v
		}
	}()
	return out
}

func main() {
	sum := 0
	for v := range square(producer(5)) { // pipeline: producer -> square -> main
		sum += v
	}
	fmt.Println("sum of squares 1..5 =", sum)

	buf := make(chan string, 2) // buffered: sends do not block until full
	buf <- "a"
	buf <- "b"
	fmt.Println(len(buf), cap(buf), <-buf, <-buf)
}

Output:

sum of squares 1..5 = 55
2 2 a b

Only the sender closes

Closing from the receiving side, or closing twice, panics; give ownership of closing to the sender.

Quick check: What ends a for v := range ch loop?

  • The channel being closed and drained
  • Receiving a zero value
  • A timeout by default
  • The buffer becoming full
Answer

The channel being closed and drained — Close signals completion.