A channel is Go’s typed mechanism for passing values between goroutines. It provides both communication and synchronization: one goroutine sends a value, another receives it, and their progress can depend on that exchange. This supports a message-passing design in which goroutines keep isolated state instead of directly modifying shared memory. section 1.5.1 section 7.1 section 9.1.2
Listing 7.1 Creating and using a channel
package main import "fmt" func main() { msgChannel := make(chan string) #1 go receiver(msgChannel) #2 fmt.Println("Sending HELLO...") msgChannel <- "HELLO" #3 fmt.Println("Sending THERE...") #3 msgChannel <- "THERE" #3 fmt.Println("Sending STOP...") #3 msgChannel <- "STOP" #3 } #1 Creates a new channel of type string #2 Starts a new goroutine with a reference to the channel #3 Sends three string messages over the channel
Create a typed channel with make, pass it to a goroutine, and send values with <-; receiving reverses the operator’s position. section 7.1.1
Unbuffered channels are synchronous by default. A send blocks until a receiver is ready, and a receive blocks until a value arrives. That blocking is useful for coordinating work, but a sender with no receiver—or a receiver with no sender—can leave every goroutine asleep and cause a deadlock. section 7.1.1 section 7.4
Channels can coordinate common concurrent tasks:
- Send typed messages between two or more goroutines. section 7.1.1
- Buffer up to N messages so senders can proceed until the buffer fills, while receivers can proceed until it empties. section 7.4
- Return results from background work by having a goroutine place a function result on an output channel. section 7.1.5
- Restrict a parameter to receive-only or send-only channel use when its role should be one-directional. section 7.4
Collecting concurrent results
A factor-finding task can run in an anonymous goroutine and send its []int result on resultCh. Meanwhile, the main goroutine computes another factor list. Reading <-resultCh later collects the background result; if it is not ready, the read waits. This avoids using a shared result variable plus a waitgroup for that exchange. section 7.1.5
Closing a channel signals that no more values will be sent. Sending after close raises an error. A receive from a closed channel yields the type’s default value, so use the second receive result, msg, more := <-ch, or range over the channel to distinguish closure and stop cleanly. A sentinel value can signal termination too, but may collide with valid data. section 7.1.4 section 7.1.4
Channels implement Go’s CSP-inspired model: goroutines exchange copies of values through synchronized channels rather than sharing mutable memory. This often reduces race-condition risk, though mutexes, condition variables, or other shared-memory primitives can be a better-performing fit for some problems. Avoid passing mutable pointers on channels; prefer copies, immutable data, or sender relinquishment of a reference. section 1.5.2 section 9.2
Dynamic prime pipeline
Because channels are first-class values, a prime-filter goroutine can create a new channel and pass it to the next filter. The resulting pipeline grows as new primes are found, passes surviving numbers onward, closes downstream channels when input ends, and uses a quit channel to signal completion. section 9.2.7
