Go Channels
A channel is a pipe that connects goroutines. One goroutine sends a value into the channel; another receives it. Channels provide safe communication between concurrent goroutines without shared memory conflicts.
Go's philosophy: Do not communicate by sharing memory; share memory by communicating.
Creating and Using a Channel
package main
import "fmt"
func main() {
ch := make(chan int) // create a channel that carries int values
go func() {
ch <- 42 // send 42 into the channel
}()
value := <-ch // receive from the channel
fmt.Println(value) // 42
}
Channel Operations Diagram
Goroutine A Goroutine B
│ │
│ ch := make(chan int) │
│ │
├─── ch <- 42 ─────────────────►│
│ (send) │ value := <-ch
│ │ (receive)
│ │
│ ▼
value = 42
Channel Direction
| Syntax | Meaning |
|---|---|
ch <- val | Send val into channel ch |
val := <-ch | Receive from channel ch, store in val |
<-ch | Receive and discard the value |
Unbuffered vs Buffered Channels
Unbuffered Channel (default)
Sender blocks until receiver is ready. Receiver blocks until sender sends. Both goroutines must be ready at the same time.
ch := make(chan string) // unbuffered
Buffered Channel
A buffered channel holds a specified number of values without blocking. The sender only blocks when the buffer is full. The receiver only blocks when the buffer is empty.
package main
import "fmt"
func main() {
ch := make(chan string, 3) // buffer holds 3 strings
ch <- "first"
ch <- "second"
ch <- "third"
// no goroutine needed — buffer absorbs the sends
fmt.Println(<-ch) // first
fmt.Println(<-ch) // second
fmt.Println(<-ch) // third
}
Passing Channels to Functions
Channels can be passed to functions with directional types to enforce send-only or receive-only access.
package main
import "fmt"
func producer(ch chan<- int) { // send-only channel
for i := 1; i <= 5; i++ {
ch <- i
}
close(ch)
}
func consumer(ch <-chan int) { // receive-only channel
for val := range ch {
fmt.Println("Received:", val)
}
}
func main() {
ch := make(chan int)
go producer(ch)
consumer(ch)
}
Output:
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Closing a Channel and range
A sender closes a channel with close(ch) to signal no more values will be sent. A receiver can range over a channel — the loop automatically ends when the channel is closed.
close(ch) // sender closes the channel
for val := range ch { // receiver loops until channel is closed
fmt.Println(val)
}
Buffered vs Unbuffered Comparison
| Feature | Unbuffered | Buffered |
|---|---|---|
| Created with | make(chan T) | make(chan T, n) |
| Blocks sender | Until receiver ready | Only when buffer is full |
| Blocks receiver | Until sender sends | Only when buffer is empty |
| Synchronization | Strict — both sides sync | Looser — decoupled |
Key Points
- Channels connect goroutines for safe communication
- Use
<-to send into and receive from a channel - Unbuffered channels synchronize both sides; buffered channels decouple them
- Only the sender should close a channel, never the receiver
- Use
rangeto receive all values until a channel is closed - Directional channel types (
chan<-,<-chan) enforce usage at compile time
