liveBook · Manning

Channel

As explained in 100 Go Mistakes and How to Avoid Them

A channel is a Go synchronization and communication mechanism through which goroutines send and receive typed values.

A channel carries typed values between goroutines. It is useful for coordinating work, but using a channel does not automatically make shared data safe. When parallel goroutines need to protect access to a shared resource, a mutex is generally the better fit. section 8.3

Choose the channel’s capacity for the behavior you need:

  • Unbuffered: make(chan int) has no capacity. A send blocks until a receiver receives the value, providing a synchronization point. section 9.7
  • Buffered: make(chan int, 1) lets a send proceed while space remains. A send blocks when the buffer is full; an available slot is not a rendezvous with a receiver. section 9.7

A buffered send is not an acknowledgment

Suppose one goroutine assigns i = 1 and then receives from ch, while another sends to ch and prints i. With make(chan struct{}, 1), the send can finish before the assignment: the print races with the write. Changing ch to make(chan struct{}) makes the send wait for the receive. The assignment then happens before the print. A send also happens before its corresponding receive completes, so channel operations can establish ordering—but the direction and buffering matter. section 8.4.2

i := 0
ch := make(chan struct{}, 1)
go func() {
    i = 1
    <-ch
}()
ch <- struct{}{}
fmt.Println(i)

Inspect the buffered version: the send need not wait for the goroutine that writes i. section 8.4.2

i := 0
ch := make(chan struct{})   #1
go func() {
    i = 1
    <-ch
}()
ch <- struct{}{}
fmt.Println(i)

Compare the unbuffered version: the send waits for the receive after the write. section 8.4.2

Case order does not prioritize a channel

A loop receiving messages and a disconnection notification might put the message case first in select, expecting to process every queued message before returning. If both receives can proceed, select chooses one pseudo-randomly; the disconnect case can win while messages remain. If queued messages must be handled before exit, receive the disconnect notification and then use a second select loop to drain available messages, returning when its default case runs. section 9.4

Use channels to communicate results or notifications between goroutines. Use an unbuffered channel when the send–receive meeting is required for synchronization; choose buffering when senders should be able to proceed without an immediate receiver. Specify buffer capacity deliberately rather than treating a larger buffer as a synchronization fix. section 8.3 section 9.7

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Works through blocking, capacity, memory-order guarantees, and select behavior rather than stopping at the channel definition.
Examples
Contrasts buffered and unbuffered race examples and traces a select loop that exits before consuming queued messages.
What it assumes
Assumes familiarity with goroutines and select while explaining channel capacity and blocking from their basic behavior.
What you can do after
Diagnose/debug channel-related races and premature exits, then choose synchronization or draining behavior to match the requirement.
How it connects
Connects channels to goroutines, mutexes, the Go memory model, data races, and select.

Also explained in

Compare how 10 books explain channel →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.