liveBook · Manning

Channel

As explained in Learn Go with Pocket-Sized Projects

A channel is a Go communication mechanism that allows goroutines to exchange typed values and synchronize execution.

A channel lets Go goroutines exchange values or signals. Use one when a goroutine needs to communicate data, an error, or completion—not merely because code runs concurrently. A channel has a message type, is created with make, and uses <- to send and receive. section 7.2.3 section 7.5.1

Choose the buffer with the sender and receiver in mind:

  • With make(chan int), a send waits for a corresponding receive. A receive with no available message also waits. section 7.2.3 section 9.3.4
  • With make(chan int, 10), sends can fill a ten-value buffer before a full buffer blocks further sends. Values are received in the order sent. section 7.2.3
  • Close a channel when no more messages will be sent. Receivers can still read from a closed channel. section 7.2.3
c := make(chan struct{}, 1) #1

go func(doneChan chan <- struct{}) {
    defer func() { #2
        log.Println("done")
        doneChan <- struct{}{}
        close(doneChan) #3
    } ()
    // run task
}(c) #4

_ = <- c #5

A buffered chan struct{} carries a completion signal without meaningful data. The caller receives from it to wait for the goroutine; the sender's channel parameter permits sends only. section 7.2.3 section 7.6

A blocked maze solver

Suppose a solver sends its first path before it starts listening for paths. If its channel is nil, the program deadlocks; initializing an unbuffered channel still leaves that first send waiting for a receiver. A one-value buffer lets the solver publish the initial path and then start listening. Later, a listener that waits forever after exploration ends needs a separate stopping condition. section 9.3.4

In a concurrent maze solver, explorers send new paths to a listener that starts more goroutines. A separate quit channel can signal that a solution was found. Use a for-select loop to receive from both channels; when multiple cases are ready, Go chooses one at random. A sync.WaitGroup can then wait for launched explorers to finish. section 9.3.2 section 9.5.2

Listing 9.23 explore_internal_test.go: Testing the explore function
func TestSolver_explore(t *testing.T) {
    tests := map[string]struct {
        inputImage string #1
        wantSize   int #2
    }{
        "cross": {
            inputImage: "testdata/explore_cross.png",
            wantSize:   2,
        },
        // ...
    }
    for name, tt := range tests {
        name, tt := name, tt

        t.Run(name, func(t *testing.T) {
            t.Parallel() #3

            maze, err := openMaze(tt.inputImage) #4
            require.NoError(t, err)

            s := &Solver{
                maze:           maze,
                palette:        defaultPalette(),
                pathsToExplore: make(chan *path, 3), #5
            }

            s.explore(&path{at: image.Point{0, 2}}) #6

            assert.Equal(t, 
                tt.wantSize, len(s.pathsToExplore)) #7
        })
    }
}

To test an explorer without running its listener, give the channel enough buffer space for every expected branch, then inspect the number of published paths. Avoid making the test depend on an unspecified neighbor order. section 9.3.6

A channel need not be the answer to every synchronization problem: sync.WaitGroup waits for goroutines when errors need no special handling, while errgroup.Group supports waiting for errors. Reserve channels for communication into or out of goroutines. section 7.5.1 section 7.6

How Learn Go with Pocket-Sized Projects covers it

How deep it goes
Works through typed sends, receives, buffering, blocking, closing, and select; defers a deeper account of unbuffered channels.
Examples
Demonstrates completion signals, maze-branch dispatch and testing, and an empty-channel receive that keeps a WebAssembly program running.
What it assumes
Introduces channel syntax and behavior from first use while building on functions launched as goroutines.
What you can do after
Apply it via Go channels to coordinate goroutines, choose buffer capacity, and diagnose blocked sends or listeners.
How it connects
Connects channels to goroutines, select, deadlocks, sync.WaitGroup, and errgroup.Group.

Also explained in

Compare how 10 books explain channel →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Go with Pocket-Sized Projects. The section links point to the parts of the book it draws on.