100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Go

Channels in Go

Channels are typed conduits that let goroutines safely send and receive values, embodying Go's share-memory-by-communicating philosophy.

ConcurrencyBeginner8 min readJul 8, 2026
Analogies

Introduction

Channels are Go's built-in mechanism for goroutines to communicate and synchronize. Instead of sharing memory and protecting it with locks, Go encourages passing data through channels: "Don't communicate by sharing memory; share memory by communicating." A channel is a typed pipe you can send values into and receive values from, and by default this send/receive is synchronous, which naturally coordinates goroutines without extra locking.

🏏

Cricket analogy: Instead of two batsmen shouting across the pitch and risking a mix-up, they run between the wickets using a clear call-and-response, just as goroutines use a channel's send/receive to synchronize instead of sharing a scoreboard directly.

Syntax

go
ch := make(chan int)       // unbuffered channel
bch := make(chan int, 5)   // buffered channel with capacity 5

ch <- 42        // send a value into the channel
v := <-ch       // receive a value from the channel
close(ch)       // close the channel; no more sends allowed

for v := range ch {
    // receives values until the channel is closed
}

Explanation

An unbuffered channel (make(chan int)) has no capacity: a send blocks until another goroutine is ready to receive, and vice versa. This makes unbuffered channels a synchronization point as well as a data pipe. A buffered channel (make(chan int, 5)) can hold up to 5 values without a receiver present; sends only block once the buffer is full, and receives block only when it's empty. Closing a channel with close(ch) signals that no more values will be sent; receivers can keep draining any buffered values, and for v := range ch automatically stops once the channel is closed and drained. Sending on a closed channel causes a panic, so only the sender should close a channel.

🏏

Cricket analogy: An unbuffered channel is like a strict single-run between wickets where the striker and non-striker must both commit at once; a buffered channel of 5 is like a bowler who can stack up to 5 deliveries in an over before the umpire calls dead ball, and close(ch) is the umpire signaling declaration so range stops reading the scorecard once the innings ends.

Example

go
package main

import "fmt"

func produce(ch chan<- int, count int) {
    for i := 1; i <= count; i++ {
        ch <- i * i
    }
    close(ch)
}

func main() {
    ch := make(chan int, 3)
    go produce(ch, 5)

    for v := range ch {
        fmt.Println("received:", v)
    }
    fmt.Println("channel closed, done")
}

Output

received: 1 received: 4 received: 9 received: 16 received: 25 channel closed, done Because the producer closes the channel after sending all values, range exits cleanly once every buffered value has been received.

🏏

Cricket analogy: The clean sequence of received squares (1, 4, 9, 16, 25) followed by a closing message is like a scorer logging each ball's total before the umpire calls stumps, so the tally sheet closes only after every run is recorded.

Key Takeaways

  • Channels are typed pipes for sending and receiving values between goroutines.
  • Unbuffered channels (make(chan T)) synchronize sender and receiver; sends block until received.
  • Buffered channels (make(chan T, n)) allow up to n values to queue before a send blocks.
  • close(ch) signals no more values will be sent; only the sender should close a channel.
  • for v := range ch reads values until the channel is closed and drained.
  • Go favors sharing memory by communicating over channels rather than shared variables and locks.

Practice what you learned

Was this page helpful?

Topics covered

#Go#GoProgrammingStudyNotes#Programming#ChannelsInGo#Channels#Syntax#Explanation#Example#StudyNotes#SkillVeris