liveBook · Manning

Channel

As explained in Learn Concurrent Programming with Go

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

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

How Learn Concurrent Programming with Go covers it

How deep it goes
Explains creation, typed sends and receives, blocking, buffers, directions, closure, CSP patterns, and a queue-and-semaphore implementation.
Examples
Uses string-message exchange, blocked sender and receiver failures, channel closure, factor calculation, and a prime-number pipeline.
What it assumes
Introduces channels alongside goroutines and contrasts them with memory-sharing synchronization primitives.
What you can do after
Apply it via Go channels to exchange messages, synchronize goroutines, collect results, close streams, and build dynamic pipelines.
How it connects
Connects channels with goroutines, CSP, message passing, race conditions, mutexes, semaphores, condition variables, and select.

Related concepts

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